/*!
 * LMV v6.4.1
 * 
 * Copyright 2019 Autodesk, Inc.
 * All rights reserved.
 * 
 * This computer source code and related instructions and comments are the
 * unpublished confidential and proprietary information of Autodesk, Inc.
 * and are protected under Federal copyright and state trade secret law.
 * They may not be disclosed to, copied or used by any third party without
 * the prior written consent of Autodesk, Inc.
 * 
 * Autodesk Forge Viewer Usage Limitations:
 * 
 * The Autodesk Forge viewer can only be used to view files generated by
 * Autodesk Forge services. The Autodesk Forge Viewer JavaScript must be
 * delivered from an Autodesk hosted URL.
 */
Autodesk.Extensions.Section =
/******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId]) {
/******/ 			return installedModules[moduleId].exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ 		}
/******/ 	};
/******/
/******/ 	// define __esModule on exports
/******/ 	__webpack_require__.r = function(exports) {
/******/ 		if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ 			Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ 		}
/******/ 		Object.defineProperty(exports, '__esModule', { value: true });
/******/ 	};
/******/
/******/ 	// create a fake namespace object
/******/ 	// mode & 1: value is a module id, require it
/******/ 	// mode & 2: merge all properties of value into the ns
/******/ 	// mode & 4: return value when already ns object
/******/ 	// mode & 8|1: behave like require
/******/ 	__webpack_require__.t = function(value, mode) {
/******/ 		if(mode & 1) value = __webpack_require__(value);
/******/ 		if(mode & 8) return value;
/******/ 		if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ 		var ns = Object.create(null);
/******/ 		__webpack_require__.r(ns);
/******/ 		Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ 		if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ 		return ns;
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__webpack_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__webpack_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";
/******/
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = "./extensions/Section/Section.js");
/******/ })
/************************************************************************/
/******/ ({

/***/ "./extensions/CompGeom/complex-polygon.js":
/*!************************************************!*\
  !*** ./extensions/CompGeom/complex-polygon.js ***!
  \************************************************/
/*! exports provided: ComplexPolygon */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ComplexPolygon", function() { return ComplexPolygon; });
/* harmony import */ var _poly2tri__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./poly2tri */ "./extensions/CompGeom/poly2tri.js");
/* harmony import */ var _poly2tri__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_poly2tri__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _interval_tree__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./interval-tree */ "./extensions/CompGeom/interval-tree.js");
function _classCallCheck(instance, Constructor) {if (!(instance instanceof Constructor)) {throw new TypeError("Cannot call a class as a function");}}function _defineProperties(target, props) {for (var i = 0; i < props.length; i++) {var descriptor = props[i];descriptor.enumerable = descriptor.enumerable || false;descriptor.configurable = true;if ("value" in descriptor) descriptor.writable = true;Object.defineProperty(target, descriptor.key, descriptor);}}function _createClass(Constructor, protoProps, staticProps) {if (protoProps) _defineProperties(Constructor.prototype, protoProps);if (staticProps) _defineProperties(Constructor, staticProps);return Constructor;}




function copyPoints(pts, bbox) {

  //Moving poiints to be centered on the origin
  //seems to improve triangulation success rate, or
  //at least avoids some bugs in poly2yti

  var sz = bbox.size();
  var offsetx = bbox.min.x + sz.x * 0.5;
  var offsety = bbox.min.y + sz.y * 0.5;
  var scale = 2.0 / sz.length();

  var pts2 = [];

  for (var i = 0, iEnd = pts.length; i < iEnd; i++) {
    pts2.push({
      x: (pts[i].x - offsetx) * scale, // + (Math.random()-0.5) * 1e-9,
      y: (pts[i].y - offsety) * scale, // + (Math.random()-0.5) * 1e-9,
      _triidx: i + 1 });

  }

  return pts2;

}


//Represents a polygon with holes, and provides triangulation and mesh conversion utilities
var ComplexPolygon = /*#__PURE__*/function () {

  function ComplexPolygon(uniquePoints, customInsidechecker, bbox) {_classCallCheck(this, ComplexPolygon);
    this.pts = uniquePoints;
    this.contours = [];
    this.customInsideChecker = customInsidechecker;
    this.bbox = bbox;
  }_createClass(ComplexPolygon, [{ key: "addContour", value: function addContour(

    indices) {
      this.contours.push(indices);
    } }, { key: "pointInContour", value: function pointInContour(

    x, y, cntr) {
      var yflag0, yflag1;
      var vtx0X, vtx0Y, vtx1X, vtx1Y;

      var inside_flag = false;

      var pts = this.pts;

      // get the last point in the polygon
      vtx0X = pts[cntr[cntr.length - 1]].x;
      vtx0Y = pts[cntr[cntr.length - 1]].y;

      // get test bit for above/below X axis
      yflag0 = vtx0Y >= y;

      for (var j = 0, jEnd = cntr.length; j < jEnd; ++j)
      {
        vtx1X = pts[cntr[j]].x;
        vtx1Y = pts[cntr[j]].y;

        yflag1 = vtx1Y >= y;

        // Check if endpoints straddle (are on opposite sides) of X axis
        // (i.e. the Y's differ); if so, +X ray could intersect this edge.
        // The old test also checked whether the endpoints are both to the
        // right or to the left of the test point.  However, given the faster
        // intersection point computation used below, this test was found to
        // be a break-even proposition for most polygons and a loser for
        // triangles (where 50% or more of the edges which survive this test
        // will cross quadrants and so have to have the X intersection computed
        // anyway).  I credit Joseph Samosky with inspiring me to try dropping
        // the "both left or both right" part of my code.
        if (yflag0 != yflag1)
        {
          // Check intersection of pgon segment with +X ray.
          // Note if >= point's X; if so, the ray hits it.
          // The division operation is avoided for the ">=" test by checking
          // the sign of the first vertex wrto the test point; idea inspired
          // by Joseph Samosky's and Mark Haigh-Hutchinson's different
          // polygon inclusion tests.
          if ((vtx1Y - y) * (vtx0X - vtx1X) >=
          (vtx1X - x) * (vtx0Y - vtx1Y) == yflag1)
          {
            inside_flag = !inside_flag;
          }
        }

        // move to the next pair of vertices, retaining info as possible
        yflag0 = yflag1;
        vtx0X = vtx1X;
        vtx0Y = vtx1Y;
      }

      return inside_flag;
    } }, { key: "pointInPolygon", value: function pointInPolygon(


    x, y) {
      var inside = false;

      for (var i = 0; i < this.contours.length; i++) {

        if (this.pointInContour(x, y, this.contours[i]))
        inside = !inside;
      }

      return inside;
    } }, { key: "triangulate", value: function triangulate()

    {
      try {
        this.triangulateInternal();
      } catch (e) {
        //this.indices = null;
        console.warn("Triangulation failed", e);
        this.triangulationFailed = true;
      }
    } }, { key: "createPointInPolygonChecker", value: function createPointInPolygonChecker()

    {

      var edges = [];

      for (var i = 0; i < this.contours.length; i++) {
        var cntr = this.contours[i];

        var len = cntr.length;
        for (var k = 0; k < len - 1; k++) {
          var e = {
            p1: cntr[k],
            p2: cntr[k + 1] };

          edges.push(e);
        }
      }

      var it = new _interval_tree__WEBPACK_IMPORTED_MODULE_1__["IntervalTree"](this.pts, edges, this.bbox);
      it.build();
      this.customInsideChecker = it;
    } }, { key: "triangulateInternal", value: function triangulateInternal()

    {

      if (!this.contours.length) {
        this.triangulationFailed = true;
        this.indices = null;
        return;
      }

      this.indices = [];

      var _pts = copyPoints(this.pts, this.bbox);

      var sweepCtx = new _poly2tri__WEBPACK_IMPORTED_MODULE_0__["SweepContext"]([]);

      sweepCtx.points_ = _pts;

      for (var i = 0; i < this.contours.length; i++) {
        var cntr = this.contours[i];

        //Contour is not closed
        var isOpen = cntr[0] !== cntr[cntr.length - 1];

        //if (isOpen)
        //    continue;

        var len = isOpen ? cntr.length : cntr.length - 1;
        var edge = new Array(len);
        for (var k = 0; k < len; k++) {
          edge[k] = _pts[cntr[k]];
        }

        sweepCtx.initEdges(edge, isOpen);
      }

      sweepCtx.triangulate();

      this.processResult(sweepCtx);

      this.triangulationFailed = !this.indices || !this.indices.length;

    } }, { key: "processResult", value: function processResult(

    sweepCtx) {

      //If the polygon has a lot of vertices, create
      //an acceleration structure for point-in-polygon checks
      //so we can filter the triangles faster.
      if (this.pts.length > 10)
      this.createPointInPolygonChecker();

      var tris = sweepCtx.map_;
      for (var i = 0; i < tris.length; i++) {
        var tpts = tris[i].points_;
        var p0 = tpts[0];
        var p1 = tpts[1];
        var p2 = tpts[2];

        var i0 = p0._triidx;
        var i1 = p1._triidx;
        var i2 = p2._triidx;

        if (i0 && i1 && i2)
        this.filterFace(i0 - 1, i1 - 1, i2 - 1);

      }
    } }, { key: "filterFace", value: function filterFace(


    i0, i1, i2) {

      var p0 = this.pts[i0];
      var p1 = this.pts[i1];
      var p2 = this.pts[i2];

      var cx = (p0.x + p1.x + p2.x) / 3;
      var cy = (p0.y + p1.y + p2.y) / 3;

      var inside = this.customInsideChecker ? this.customInsideChecker.pointInPolygon(cx, cy) : this.pointInPolygon(cx, cy);

      if (inside) {

        var e1x = p1.x - p0.x;
        var e1y = p1.y - p0.y;
        var e2x = p2.x - p0.x;
        var e2y = p2.y - p0.y;

        var cross = e1x * e2y - e2x * e1y;

        if (cross > 0) {
          this.indices.push(i0, i1, i2);
        } else {
          this.indices.push(i0, i2, i1);
        }

      }
    }

    //creates a vertex buffer containing a filled 2D polygon for visualization on the cut plane
    //as 2D polygon mesh in the 3D model space
  }, { key: "toPolygonMesh", value: function toPolygonMesh(packNormals) {

      if (this.polygonMesh)
      return this.polygonMesh;

      var pts = this.pts;

      var bg = new THREE.BufferGeometry();

      var pos = new Float32Array(3 * pts.length);
      for (var j = 0; j < pts.length; j++) {
        pos[3 * j] = pts[j].x;
        pos[3 * j + 1] = pts[j].y;
        pos[3 * j + 2] = 0;
      }
      bg.addAttribute("position", new THREE.BufferAttribute(pos, 3));

      var normal = packNormals ? new Uint16Array(2 * pts.length) : new Float32Array(3 * pts.length);

      for (var _j = 0; _j < pts.length; _j++) {

        if (packNormals) {
          var pnx = (0 /*Math.atan2(0, 0)*/ / Math.PI + 1.0) * 0.5;
          var pny = (1.0 + 1.0) * 0.5;

          normal[_j * 2] = pnx * 65535 | 0;
          normal[_j * 2 + 1] = pny * 65535 | 0;
        } else {
          normal[3 * _j] = 0;
          normal[3 * _j + 1] = 0;
          normal[3 * _j + 2] = 1;
        }
      }

      bg.addAttribute("normal", new THREE.BufferAttribute(normal, packNormals ? 2 : 3));
      if (packNormals) {
        bg.attributes.normal.bytesPerItem = 2;
        bg.attributes.normal.normalize = true;
      }

      var index = new Uint16Array(this.indices.length);
      index.set(this.indices);

      bg.addAttribute("index", new THREE.BufferAttribute(index, 1));

      bg.streamingDraw = true;
      bg.streamingIndex = true;

      this.polygonMesh = bg;

      return bg;
    }


    //creates an extruded polygon 3d mesh
    //with the given thickness (maxZ=0, minZ=-thickness)
  }, { key: "toExtrudedMesh", value: function toExtrudedMesh(thickness) {

      if (this.extrudedMesh)
      return this.extrudedMesh;

      if (thickness === undefined)
      thickness = 1;

      //TODO: in case of failed triangulation
      //we can still generate a tube mesh with just the sides, without top and bottom caps
      if (!this.indices)
      return null;

      var vb = [];
      var indices = [];
      var iblines = [];
      var vbase = 0;

      //TODO: for better performance we can allocate ArrayBuffers up front with known
      //sizes... once the logic works.

      //Add the top and bottom polygons

      //The top is just the already triangulated 2D polygon
      //same as toPolygonMesh

      var pts = this.pts;
      for (var i = 0; i < pts.length; i++) {
        vb.push(pts[i].x, pts[i].y, 0);
        vb.push(0, 0, 1);
      }

      var inds = this.indices;

      for (var _i = 0; _i < inds.length; _i += 3) {
        indices.push(inds[_i], inds[_i + 1], inds[_i + 2]);
      }

      vbase += pts.length;

      //The bottom is like the top, but mirrored.

      for (var _i2 = 0; _i2 < pts.length; _i2++) {
        vb.push(pts[_i2].x, pts[_i2].y, -thickness);
        vb.push(0, 0, -1);
      }

      for (var _i3 = 0; _i3 < inds.length; _i3 += 3) {
        indices.push(vbase + inds[_i3], vbase + inds[_i3 + 2], vbase + inds[_i3 + 1]);
      }

      vbase += pts.length;

      //The sides -- each segment of the contours becomes a quad

      var tmp = new Vector3();
      var bbox = new Box3();

      for (var j = 0; j < this.contours.length; j++) {
        var cntr = this.contours[j];

        for (var _i4 = 0; _i4 < cntr.length - 1; _i4++) {

          //Add quad for each face formed by the extruded contour
          var x1 = this.pts[cntr[_i4]].x;
          var y1 = this.pts[cntr[_i4]].y;
          var z1 = 0;

          tmp.set(x1, y1, z1);
          bbox.expandByPoint(tmp);

          var x2 = this.pts[cntr[_i4 + 1]].x;
          var y2 = this.pts[cntr[_i4 + 1]].y;
          var z2 = 0;

          tmp.set(x2, y2, z2);
          bbox.expandByPoint(tmp);

          tmp.set(x1, y1, z1 - thickness);
          bbox.expandByPoint(tmp);

          //orthogonal to the face, will use for the normals
          tmp.set(y1 - y2, x2 - x1, 0).normalize();

          iblines.push(vbase, vbase + 1, vbase, vbase + 2, vbase + 1, vbase + 3, vbase + 2, vbase + 3);

          //If it's a hole, normals and faces are inverted
          if (j > 0) {

            vb.push(x1, y1, z1, -tmp.x, -tmp.y, -tmp.z,
            x2, y2, z2, -tmp.x, -tmp.y, -tmp.z,
            x1, y1, z1 - thickness, -tmp.x, -tmp.y, -tmp.z,
            x2, y2, z2 - thickness, -tmp.x, -tmp.y, -tmp.z);

            indices.push(vbase, vbase + 3, vbase + 2, vbase, vbase + 1, vbase + 3);
          } else {
            vb.push(x1, y1, z1, tmp.x, tmp.y, tmp.z,
            x2, y2, z2, tmp.x, tmp.y, tmp.z,
            x1, y1, z1 - thickness, tmp.x, tmp.y, tmp.z,
            x2, y2, z2 - thickness, tmp.x, tmp.y, tmp.z);

            indices.push(vbase, vbase + 2, vbase + 3, vbase + 0, vbase + 3, vbase + 1);
          }

          vbase += 4;
        }
      }

      //Convert to mesh suitable for rendering
      //TODO: As mentioned above, we can do this directly in the loop above
      //for better performance.

      var vbp = new Float32Array(vb.length);
      vbp.set(vb);

      var vbi = new Uint16Array(indices.length);
      vbi.set(indices);

      var vbili = new Uint16Array(iblines.length);
      vbili.set(iblines);

      var mdata = {
        mesh: {
          vb: vbp,
          indices: vbi,
          iblines: vbili,

          vbstride: 6,
          vblayout: {
            position: { offset: 0, itemSize: 3, bytesPerItem: 4 },
            normal: { offset: 3, itemSize: 3, bytesPerItem: 4 } },

          boundingBox: bbox,
          boundingSphere: { center: bbox.center(), radius: bbox.size().length * 0.5 } } };



      avp.BufferGeometryUtils.meshToGeometry(mdata);

      mdata.geometry.streamingDraw = true;
      mdata.geometry.streamingIndex = true;

      this.extrudedMesh = mdata.geometry;

      return this.extrudedMesh;
    } }]);return ComplexPolygon;}();

/***/ }),

/***/ "./extensions/CompGeom/edge-set.js":
/*!*****************************************!*\
  !*** ./extensions/CompGeom/edge-set.js ***!
  \*****************************************/
/*! exports provided: EdgeSet */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "EdgeSet", function() { return EdgeSet; });
/* harmony import */ var _interval_tree__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./interval-tree */ "./extensions/CompGeom/interval-tree.js");
/* harmony import */ var _point_list__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./point-list */ "./extensions/CompGeom/point-list.js");
/* harmony import */ var _complex_polygon__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./complex-polygon */ "./extensions/CompGeom/complex-polygon.js");
function _classCallCheck(instance, Constructor) {if (!(instance instanceof Constructor)) {throw new TypeError("Cannot call a class as a function");}}function _defineProperties(target, props) {for (var i = 0; i < props.length; i++) {var descriptor = props[i];descriptor.enumerable = descriptor.enumerable || false;descriptor.configurable = true;if ("value" in descriptor) descriptor.writable = true;Object.defineProperty(target, descriptor.key, descriptor);}}function _createClass(Constructor, protoProps, staticProps) {if (protoProps) _defineProperties(Constructor.prototype, protoProps);if (staticProps) _defineProperties(Constructor, staticProps);return Constructor;}



var avp = Autodesk.Viewing.Private;

//Functionality for converting a list of two-point line segments into a connected
//set of (hopefully) closed contour lines. The contour set is then used
//for triangulation.
//This data structure assumes there are no intersecting edges (use the DCEL if there are, or you need fully-connected topology).
var EdgeSet = /*#__PURE__*/function () {

  function EdgeSet(edges, bbox, precisionTolerance) {_classCallCheck(this, EdgeSet);

    this.edges = edges;
    this.bbox = bbox;

    this.verts = new _point_list__WEBPACK_IMPORTED_MODULE_1__["UniquePointList"](bbox, precisionTolerance);
    this.polygon = null;
  }_createClass(EdgeSet, [{ key: "getPointIndex", value: function getPointIndex(


    px, py) {

      var v = this.verts.findOrAddPoint(px, py);

      return v.id;
    } }, { key: "snapEdges", value: function snapEdges()

    {

      for (var i = 0; i < this.edges.length; i++) {

        var e = this.edges[i];

        e.p1 = this.getPointIndex(e.v1.x, e.v1.y);
        e.p2 = this.getPointIndex(e.v2.x, e.v2.y);
      }
    } }, { key: "sanitizeEdges", value: function sanitizeEdges()

    {
      var edgeSet = {};
      var sanitizedEdges = [];

      for (var i = 0, len = this.edges.length; i < len; i++) {
        var e = this.edges[i];
        if (e.p1 === e.p2) {
          continue;
        }

        var key = Math.min(e.p1, e.p2) + ':' + Math.max(e.p1, e.p2);
        if (edgeSet[key] !== true) {
          edgeSet[key] = true;
          sanitizedEdges.push(e);
        }
      }

      this.edges = sanitizedEdges;
    } }, { key: "stitchContours", value: function stitchContours()


    {

      this.contours = [];

      //Create jump table from edge to edge
      //and back
      var edge_table = {};

      for (var i = 0; i < this.edges.length; i++) {
        var e = this.edges[i];

        if (e.p1 === e.p2)
        continue;

        if (edge_table[e.p1] !== undefined)
        edge_table[e.p1].push(e.p2);else

        edge_table[e.p1] = [e.p2];

        if (edge_table[e.p2] !== undefined)
        edge_table[e.p2].push(e.p1);else

        edge_table[e.p2] = [e.p1];
      }

      var cur_cntr = [];

      for (var p in edge_table) {
        if (edge_table[p].length !== 2) {
          avp.logger.warn("Incomplete edge table");
          break;
        }
      }

      //Start with the first edge, and stitch until we can no longer
      while (true) {

        var sfrom = undefined;

        //Look for doubly connected point first
        for (var p in edge_table) {
          if (edge_table[p].length > 1) {
            sfrom = p;
            break;
          }
        }

        //If no double-connected point found, we know
        //the it will be an open contour, but stitch as much
        //as we can anyway.
        if (!sfrom) {
          for (var p in edge_table) {
            if (edge_table[p].length > 0) {
              sfrom = p;
              break;
            }
          }
        }

        if (!sfrom)
        break;

        var prev = -1;
        var cur = parseInt(sfrom);
        var cur_segs = edge_table[sfrom];

        //start a new contour
        cur_cntr.push(cur);

        while (cur_segs && cur_segs.length) {

          var toPt = cur_segs.shift();

          //skip backpointer if we hit it
          if (toPt === prev)
          toPt = cur_segs.shift();

          if (toPt === undefined) {
            delete edge_table[cur];
            break;
          }

          cur_cntr.push(toPt);

          if (cur_segs.length == 0)
          delete edge_table[cur];else
          if (cur_segs[0] === prev)
          delete edge_table[cur];

          prev = cur;
          cur = toPt;
          cur_segs = edge_table[toPt];
        }

        if (cur_cntr.length) {
          this.contours.push(cur_cntr);
          cur_cntr = [];
        }
      }

      var openCntrs = [];
      for (var i = 0; i < this.contours.length; i++) {
        var cntr = this.contours[i];
        if (cntr[0] !== cntr[cntr.length - 1])
        openCntrs.push(cntr);
      }


      if (openCntrs.length) {
        //avp.logger.warn("Incomplete stitch");

        var didSomething = true;
        while (didSomething) {

          didSomething = false;

          //Try to combine contours
          var cntr_edge_table = {};
          var contours = this.contours;

          for (var i = 0; i < contours.length; i++) {
            var cntr = contours[i];
            var start = cntr[0];
            var end = cntr[cntr.length - 1];

            if (start === end)
            continue;

            if (!cntr_edge_table[start])
            cntr_edge_table[start] = [-i - 1];else

            cntr_edge_table[start].push(-i - 1);


            if (!cntr_edge_table[end])
            cntr_edge_table[end] = [i];else

            cntr_edge_table[end].push(i);
          }

          for (var p in cntr_edge_table) {
            var entry = cntr_edge_table[p];

            if (entry.length == 2) {
              var toerase = undefined;

              if (entry[0] < 0 && entry[1] < 0) {
                var c1 = -entry[0] - 1;var c2 = -entry[1] - 1;
                //join start point to startpoint
                contours[c2].shift();
                Array.prototype.push.apply(contours[c1].reverse(), contours[c2]);
                toerase = c2;
              }

              if (entry[0] < 0 && entry[1] > 0) {
                var c1 = -entry[0] - 1;var c2 = entry[1];
                //join start point to endpoint
                contours[c2].pop();
                Array.prototype.push.apply(contours[c2], contours[c1]);
                toerase = c1;
              }

              if (entry[0] > 0 && entry[1] < 0) {
                var c1 = entry[0];var c2 = -entry[1] - 1;
                //join end point to startpoint
                contours[c1].pop();
                Array.prototype.push.apply(contours[c1], contours[c2]);
                toerase = c2;
              }

              if (entry[0] > 0 && entry[1] > 0) {
                var c1 = entry[0];var c2 = entry[1];
                //join end point to endpoint
                contours[c1].pop();
                Array.prototype.push.apply(contours[c1], contours[c2].reverse());
                toerase = c2;
              }

              if (toerase !== undefined) {
                contours.splice(toerase, 1);
                didSomething = true;
              }
              break;
            }
          }

        }

      }
    } }, { key: "cleanupFlatEdges", value: function cleanupFlatEdges()


    {

      var pts = this.verts.pts;
      var TOL = this.verts.precisionTolerance;

      for (var i = 0; i < this.contours.length; i++) {

        var cntr = this.contours[i];

        while (true) {


          var removePt = -1;

          for (var j = 1; j < cntr.length - 1; j++) {
            var prev = cntr[j - 1];
            var cur = cntr[j];
            var next = cntr[j + 1];

            var p0 = pts[prev];
            var p1 = pts[cur];
            var p2 = pts[next];

            var dx1 = p1.x - p0.x;
            var dy1 = p1.y - p0.y;
            var dx2 = p2.x - p1.x;
            var dy2 = p2.y - p1.y;

            var len1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
            if (len1 < TOL) {
              removePt = j;
              break;
            }

            var len2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
            if (len2 < TOL) {
              removePt = j;
              break;
            }

            dx1 /= len1;
            dy1 /= len1;
            dx2 /= len2;
            dy2 /= len2;

            var dot = dx1 * dx2 + dy1 * dy2;

            if (Math.abs(dot - 1.0) < 1e-2) {
              removePt = j;
              break;
            }
          }

          if (removePt < 0)
          break;

          cntr.splice(removePt, 1);
        }

      }

    } }, { key: "triangulate", value: function triangulate()


    {

      //this.cleanupFlatEdges();

      //The interval tree is a faster and more tolerant
      //way of checking if a point is inside the complex polygon defined
      //by a set of edges. We use that in preference to the built-in
      //ComplexPolygon inside checker.
      var it = new _interval_tree__WEBPACK_IMPORTED_MODULE_0__["IntervalTree"](this.verts.pts, this.edges, this.bbox);
      it.build();

      var polygon = new _complex_polygon__WEBPACK_IMPORTED_MODULE_2__["ComplexPolygon"](this.verts.pts, it, this.bbox);
      polygon.contours = this.contours;
      polygon.triangulate();
      return polygon;

    } }]);return EdgeSet;}();

/***/ }),

/***/ "./extensions/CompGeom/fuzzy-math.js":
/*!*******************************************!*\
  !*** ./extensions/CompGeom/fuzzy-math.js ***!
  \*******************************************/
/*! exports provided: TOL, isZero, isEqual */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TOL", function() { return TOL; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isZero", function() { return isZero; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isEqual", function() { return isEqual; });

var TOL = 1e-6;

function isZero(f) {
  return Math.abs(f) < TOL;
}

function isEqual(a, b) {
  return isZero(a - b);
}

/***/ }),

/***/ "./extensions/CompGeom/interval-tree.js":
/*!**********************************************!*\
  !*** ./extensions/CompGeom/interval-tree.js ***!
  \**********************************************/
/*! exports provided: IntervalTree */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "IntervalTree", function() { return IntervalTree; });
function IntervalNode() {

  this.bbox = new THREE.Box2();
  this.left = null;
  this.right = null;
  this.node_edges = [];
}

//Acceleration structure for point-in-polygon checking.
//Takes in a list of points and edges indexing into those points.
//The Point-in-polygon check is a simple even-odd test based on counting
//number of edges intersected by a ray from the input point to infinity.
function IntervalTree(pts, edges, bbox) {

  this.pts = pts;
  this.edges = edges;
  this.bbox = bbox;
  this.pipResult = false;

}



IntervalTree.prototype.splitNode = function (node) {

  if (node.bbox.min.y >= node.bbox.max.y)
  return;

  if (node.node_edges.length < 3)
  return;

  var split = 0.5 * (node.bbox.min.y + node.bbox.max.y);

  //node.bbox.makeEmpty();

  node.left = new IntervalNode();
  node.right = new IntervalNode();

  var pts = this.pts;
  var ne = node.node_edges;
  var remaining_node_edges = [];
  var tmpPt = new THREE.Vector2();

  for (var i = 0; i < ne.length; i++) {

    var e = this.edges[ne[i]];

    var p1y = pts[e.p1].y;
    var p2y = pts[e.p2].y;

    if (p1y > p2y) {
      var tmp = p1y;
      p1y = p2y;
      p2y = tmp;
    }

    var boxPtr = null;

    if (p2y < split) {
      node.left.node_edges.push(ne[i]);
      boxPtr = node.left.bbox;
    } else if (p1y > split) {
      node.right.node_edges.push(ne[i]);
      boxPtr = node.right.bbox;
    } else {
      remaining_node_edges.push(ne[i]);
      //boxPtr = node.bbox;
    }

    if (boxPtr) {
      tmpPt.set(pts[e.p1].x, pts[e.p1].y);
      boxPtr.expandByPoint(tmpPt);
      tmpPt.set(pts[e.p2].x, pts[e.p2].y);
      boxPtr.expandByPoint(tmpPt);
    }
  }

  node.node_edges = remaining_node_edges;

  if (node.left.node_edges.length)
  this.splitNode(node.left);
  if (node.right.node_edges.length)
  this.splitNode(node.right);
};


IntervalTree.prototype.build = function () {

  this.root = new IntervalNode();

  var edge_indices = this.root.node_edges;
  for (var i = 0; i < this.edges.length; i++) {
    edge_indices.push(i);}

  this.root.bbox.copy(this.bbox);

  //split recursively
  this.splitNode(this.root);
};




IntervalTree.prototype.pointInPolygonRec = function (node, x, y) {

  if (node.bbox.min.y <= y && node.bbox.max.y >= y) {

    var pts = this.pts;
    var ne = node.node_edges;

    for (var i = 0, iEnd = ne.length; i < iEnd; i++) {

      var e = this.edges[ne[i]];

      // get the last point in the polygon
      var p1 = pts[e.p1];
      var vtx0X = p1.x;
      var vtx0Y = p1.y;

      // get test bit for above/below X axis
      var yflag0 = vtx0Y >= y;

      var p2 = pts[e.p2];
      var vtx1X = p2.x;
      var vtx1Y = p2.y;

      var yflag1 = vtx1Y >= y;

      // Check if endpoints straddle (are on opposite sides) of X axis
      // (i.e. the Y's differ); if so, +X ray could intersect this edge.
      // The old test also checked whether the endpoints are both to the
      // right or to the left of the test point.  However, given the faster
      // intersection point computation used below, this test was found to
      // be a break-even proposition for most polygons and a loser for
      // triangles (where 50% or more of the edges which survive this test
      // will cross quadrants and so have to have the X intersection computed
      // anyway).  I credit Joseph Samosky with inspiring me to try dropping
      // the "both left or both right" part of my code.
      if (yflag0 != yflag1)
      {
        // Check intersection of pgon segment with +X ray.
        // Note if >= point's X; if so, the ray hits it.
        // The division operation is avoided for the ">=" test by checking
        // the sign of the first vertex wrto the test point; idea inspired
        // by Joseph Samosky's and Mark Haigh-Hutchinson's different
        // polygon inclusion tests.
        if ((vtx1Y - y) * (vtx0X - vtx1X) >=
        (vtx1X - x) * (vtx0Y - vtx1Y) == yflag1)
        {
          this.pipResult = !this.pipResult;
        }
      }

    }

  }

  var nl = node.left;
  if (nl && nl.bbox.min.y <= y && nl.bbox.max.y >= y) {
    this.pointInPolygonRec(nl, x, y);
  }

  var nr = node.right;
  if (nr && nr.bbox.min.y <= y && nr.bbox.max.y >= y) {
    this.pointInPolygonRec(nr, x, y);
  }

};

IntervalTree.prototype.pointInPolygon = function (x, y) {

  this.pipResult = false;

  this.pointInPolygonRec(this.root, x, y);

  return this.pipResult;

};

/***/ }),

/***/ "./extensions/CompGeom/point-list.js":
/*!*******************************************!*\
  !*** ./extensions/CompGeom/point-list.js ***!
  \*******************************************/
/*! exports provided: UniquePointList */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "UniquePointList", function() { return UniquePointList; });
/* harmony import */ var _fuzzy_math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./fuzzy-math */ "./extensions/CompGeom/fuzzy-math.js");
/* harmony import */ var _quad_tree__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./quad-tree */ "./extensions/CompGeom/quad-tree.js");
/* harmony import */ var _poly2tri__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./poly2tri */ "./extensions/CompGeom/poly2tri.js");
/* harmony import */ var _poly2tri__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_poly2tri__WEBPACK_IMPORTED_MODULE_2__);
function _classCallCheck(instance, Constructor) {if (!(instance instanceof Constructor)) {throw new TypeError("Cannot call a class as a function");}}function _defineProperties(target, props) {for (var i = 0; i < props.length; i++) {var descriptor = props[i];descriptor.enumerable = descriptor.enumerable || false;descriptor.configurable = true;if ("value" in descriptor) descriptor.writable = true;Object.defineProperty(target, descriptor.key, descriptor);}}function _createClass(Constructor, protoProps, staticProps) {if (protoProps) _defineProperties(Constructor.prototype, protoProps);if (staticProps) _defineProperties(Constructor, staticProps);return Constructor;}




var UniquePointList = /*#__PURE__*/function () {

  function UniquePointList(bbox, precisionTolerance, VertexConstructor, useQuadTree) {_classCallCheck(this, UniquePointList);

    this.bbox = bbox;
    this.boxSize = this.bbox.size().length();

    if (typeof precisionTolerance === "number") {
      //Input is in model units, e.g. if model is in feet,
      //precision tolerance has to be in feet
      this.precisionTolerance = precisionTolerance;
      this.scale = 1.0 / this.precisionTolerance;
    } else {
      this.precisionTolerance = _fuzzy_math__WEBPACK_IMPORTED_MODULE_0__["TOL"] * this.boxSize;
      this.scale = 1.0 / this.precisionTolerance;
    }

    this.snapBaseX = this.bbox.min.x; ///- 0.5 * this.precisionTolerance;
    this.snapBaseY = this.bbox.min.y; //- 0.5 * this.precisionTolerance;


    this.pts = [];
    this.xymap = {};

    if (useQuadTree)
    this.quadTreeVerts = new _quad_tree__WEBPACK_IMPORTED_MODULE_1__["QuadTree"](bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, precisionTolerance);

    this.vertexConstructor = VertexConstructor;
  }_createClass(UniquePointList, [{ key: "findOrAddPoint", value: function findOrAddPoint(

    px, py, dbIds) {

      //Snap the vertex to our desired granularity
      var x = 0 | /*Math.round*/(px - this.snapBaseX) * this.scale;
      var y = 0 | /*Math.round*/(py - this.snapBaseY) * this.scale;

      //Find the snapped vertex or create new
      var mx = this.xymap[x];
      var v;

      if (mx === undefined) {
        this.xymap[x] = mx = {};
        v = undefined;
      } else {
        v = mx[y];
      }

      if (v === undefined) {
        v = this.vertexConstructor ? new this.vertexConstructor(px, py) : new _poly2tri__WEBPACK_IMPORTED_MODULE_2__["Point"](px, py);
        mx[y] = v;
        v.id = this.pts.length;
        this.pts.push(v);

        if (this.quadTreeVerts)
        this.quadTreeVerts.addItem(v);
      }

      //Remember the source object that's adding this vertex
      if (typeof dbIds !== "undefined") {
        if (typeof dbIds === "number") {
          if (v.dbIds.indexOf(dbIds) === -1)
          v.dbIds.push(dbIds);
        } else {
          for (var i = 0; i < dbIds.length; i++) {
            var dbId = dbIds[i];
            if (v.dbIds.indexOf(dbId) === -1)
            v.dbIds.push(dbId);
          }
        }
        v.dbIdsChanged = true;
      }

      return v;
    } }, { key: "forEach", value: function forEach(


    f) {
      this.pts.forEach(f);
    } }, { key: "delete", value: function _delete(

    v) {
      this.pts[v.id] = undefined;

      if (this.quadTreeVerts)
      this.quadTreeVerts.deleteItem(v);
    }

    //filters out null entries from the point list
  }, { key: "compact", value: function compact() {

      var pts = [];

      for (var i = 0, len = this.pts.length; i < len; i++) {
        var v = this.pts[i];
        if (!v)
        continue;

        v.oldid = v.id;
        v.id = pts.length;
        pts.push(v);
      }

      this.pts = pts;

    } }, { key: "enumInBox", value: function enumInBox(

    minx, miny, maxx, maxy, f) {
      this.quadTreeVerts.enumInBox(minx, miny, maxx, maxy, f);
    } }]);return UniquePointList;}();

/***/ }),

/***/ "./extensions/CompGeom/poly2tri.js":
/*!*****************************************!*\
  !*** ./extensions/CompGeom/poly2tri.js ***!
  \*****************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/* WEBPACK VAR INJECTION */(function(global) {var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;var require;var require;function _typeof(obj) {if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {_typeof = function _typeof(obj) {return typeof obj;};} else {_typeof = function _typeof(obj) {return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;};}return _typeof(obj);}(function (f) {if (( false ? undefined : _typeof(exports)) === "object" && typeof module !== "undefined") {module.exports = f();} else if (true) {!(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (f),
				__WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ?
				(__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__),
				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));} else { var g; }})(function () {var define, module, exports;return function e(t, n, r) {function s(o, u) {if (!n[o]) {if (!t[o]) {var a = typeof require == "function" && require;if (!u && a) return require(o, !0);if (i) return i(o, !0);var f = new Error("Cannot find module '" + o + "'");throw f.code = "MODULE_NOT_FOUND", f;}var l = n[o] = { exports: {} };t[o][0].call(l.exports, function (e) {var n = t[o][1][e];return s(n ? n : e);}, l, l.exports, e, t, n, r);}return n[o].exports;}var i = typeof require == "function" && require;for (var o = 0; o < r.length; o++) {s(r[o]);}return s;}({ 1: [function (require, module, exports) {
      module.exports = { "version": "1.5.0" };
    }, {}], 2: [function (require, module, exports) {
      /*
                                                      * Poly2Tri Copyright (c) 2009-2014, Poly2Tri Contributors
                                                      * http://code.google.com/p/poly2tri/
                                                      * 
                                                      * poly2tri.js (JavaScript port) (c) 2009-2014, Poly2Tri Contributors
                                                      * https://github.com/r3mi/poly2tri.js
                                                      * 
                                                      * All rights reserved.
                                                      * 
                                                      * Distributed under the 3-clause BSD License, see LICENSE.txt
                                                      */

      /* jshint maxcomplexity:11 */

      "use strict";


      /*
                     * Note
                     * ====
                     * the structure of this JavaScript version of poly2tri intentionally follows
                     * as closely as possible the structure of the reference C++ version, to make it 
                     * easier to keep the 2 versions in sync.
                     */


      // -------------------------------------------------------------------------Node

      /**
       * Advancing front node
       * @constructor
       * @private
       * @struct
       * @param {!XY} p - Point
       * @param {Triangle=} t triangle (optional)
       */
      var Node = function Node(p, t) {
        /** @type {XY} */
        this.point = p;

        /** @type {Triangle|null} */
        this.triangle = t || null;

        /** @type {Node|null} */
        this.next = null;
        /** @type {Node|null} */
        this.prev = null;

        /** @type {number} */
        this.value = p.x;
      };

      // ---------------------------------------------------------------AdvancingFront
      /**
       * @constructor
       * @private
       * @struct
       * @param {Node} head
       * @param {Node} tail
       */
      var AdvancingFront = function AdvancingFront(head, tail) {
        /** @type {Node} */
        this.head_ = head;
        /** @type {Node} */
        this.tail_ = tail;
        /** @type {Node} */
        this.search_node_ = head;
      };

      /** @return {Node} */
      AdvancingFront.prototype.head = function () {
        return this.head_;
      };

      /** @param {Node} node */
      AdvancingFront.prototype.setHead = function (node) {
        this.head_ = node;
      };

      /** @return {Node} */
      AdvancingFront.prototype.tail = function () {
        return this.tail_;
      };

      /** @param {Node} node */
      AdvancingFront.prototype.setTail = function (node) {
        this.tail_ = node;
      };

      /** @return {Node} */
      AdvancingFront.prototype.search = function () {
        return this.search_node_;
      };

      /** @param {Node} node */
      AdvancingFront.prototype.setSearch = function (node) {
        this.search_node_ = node;
      };

      /** @return {Node} */
      AdvancingFront.prototype.findSearchNode = function () /*x*/{
        // TODO: implement BST index
        return this.search_node_;
      };

      /**
          * @param {number} x value
          * @return {Node}
          */
      AdvancingFront.prototype.locateNode = function (x) {
        var node = this.search_node_;

        /* jshint boss:true */
        if (x < node.value) {
          while (node = node.prev) {
            if (x >= node.value) {
              this.search_node_ = node;
              return node;
            }
          }
        } else {
          while (node = node.next) {
            if (x < node.value) {
              this.search_node_ = node.prev;
              return node.prev;
            }
          }
        }
        return null;
      };

      /**
          * @param {!XY} point - Point
          * @return {Node}
          */
      AdvancingFront.prototype.locatePoint = function (point) {
        var px = point.x;
        var node = this.findSearchNode(px);
        var nx = node.point.x;

        if (px === nx) {
          // Here we are comparing point references, not values
          if (point !== node.point) {
            // We might have two nodes with same x value for a short time
            if (point === node.prev.point) {
              node = node.prev;
            } else if (point === node.next.point) {
              node = node.next;
            } else {
              throw new Error('poly2tri Invalid AdvancingFront.locatePoint() call');
            }
          }
        } else if (px < nx) {
          /* jshint boss:true */
          while (node = node.prev) {
            if (point === node.point) {
              break;
            }
          }
        } else {
          while (node = node.next) {
            if (point === node.point) {
              break;
            }
          }
        }

        if (node) {
          this.search_node_ = node;
        }
        return node;
      };


      // ----------------------------------------------------------------------Exports

      module.exports = AdvancingFront;
      module.exports.Node = Node;


    }, {}], 3: [function (require, module, exports) {
      /*
                                                      * Poly2Tri Copyright (c) 2009-2014, Poly2Tri Contributors
                                                      * http://code.google.com/p/poly2tri/
                                                      *
                                                      * poly2tri.js (JavaScript port) (c) 2009-2014, Poly2Tri Contributors
                                                      * https://github.com/r3mi/poly2tri.js
                                                      *
                                                      * All rights reserved.
                                                      *
                                                      * Distributed under the 3-clause BSD License, see LICENSE.txt
                                                      */

      "use strict";

      /*
                     * Function added in the JavaScript version (was not present in the c++ version)
                     */

      /**
                         * assert and throw an exception.
                         *
                         * @private
                         * @param {boolean} condition   the condition which is asserted
                         * @param {string} message      the message which is display is condition is falsy
                         */
      function assert(condition, message) {
        if (!condition) {
          throw new Error(message || "Assert Failed");
        }
      }
      module.exports = assert;



    }, {}], 4: [function (require, module, exports) {
      /*
                                                      * Poly2Tri Copyright (c) 2009-2014, Poly2Tri Contributors
                                                      * http://code.google.com/p/poly2tri/
                                                      * 
                                                      * poly2tri.js (JavaScript port) (c) 2009-2014, Poly2Tri Contributors
                                                      * https://github.com/r3mi/poly2tri.js
                                                      * 
                                                      * All rights reserved.
                                                      * 
                                                      * Distributed under the 3-clause BSD License, see LICENSE.txt
                                                      */

      "use strict";


      /*
                     * Note
                     * ====
                     * the structure of this JavaScript version of poly2tri intentionally follows
                     * as closely as possible the structure of the reference C++ version, to make it 
                     * easier to keep the 2 versions in sync.
                     */

      var xy = require('./xy');

      // ------------------------------------------------------------------------Point
      /**
       * Construct a point
       * @example
       *      var point = new poly2tri.Point(150, 150);
       * @public
       * @constructor
       * @struct
       * @param {number=} x    coordinate (0 if undefined)
       * @param {number=} y    coordinate (0 if undefined)
       */
      var Point = function Point(x, y) {
        /**
                                         * @type {number}
                                         * @expose
                                         */
        this.x = +x || 0;
        /**
                           * @type {number}
                           * @expose
                           */
        this.y = +y || 0;

        // All extra fields added to Point are prefixed with _p2t_
        // to avoid collisions if custom Point class is used.

        /**
         * The edges this point constitutes an upper ending point
         * @private
         * @type {Array.<Edge>}
         */
        this._p2t_edge_list = null;
      };

      /**
          * For pretty printing
          * @example
          *      "p=" + new poly2tri.Point(5,42)
          *      // → "p=(5;42)"
          * @returns {string} <code>"(x;y)"</code>
          */
      Point.prototype.toString = function () {
        return xy.toStringBase(this);
      };

      /**
          * JSON output, only coordinates
          * @example
          *      JSON.stringify(new poly2tri.Point(1,2))
          *      // → '{"x":1,"y":2}'
          */
      Point.prototype.toJSON = function () {
        return { x: this.x, y: this.y };
      };

      /**
          * Creates a copy of this Point object.
          * @return {Point} new cloned point
          */
      Point.prototype.clone = function () {
        return new Point(this.x, this.y);
      };

      /**
          * Set this Point instance to the origo. <code>(0; 0)</code>
          * @return {Point} this (for chaining)
          */
      Point.prototype.set_zero = function () {
        this.x = 0.0;
        this.y = 0.0;
        return this; // for chaining
      };

      /**
          * Set the coordinates of this instance.
          * @param {number} x   coordinate
          * @param {number} y   coordinate
          * @return {Point} this (for chaining)
          */
      Point.prototype.set = function (x, y) {
        this.x = +x || 0;
        this.y = +y || 0;
        return this; // for chaining
      };

      /**
          * Negate this Point instance. (component-wise)
          * @return {Point} this (for chaining)
          */
      Point.prototype.negate = function () {
        this.x = -this.x;
        this.y = -this.y;
        return this; // for chaining
      };

      /**
          * Add another Point object to this instance. (component-wise)
          * @param {!Point} n - Point object.
          * @return {Point} this (for chaining)
          */
      Point.prototype.add = function (n) {
        this.x += n.x;
        this.y += n.y;
        return this; // for chaining
      };

      /**
          * Subtract this Point instance with another point given. (component-wise)
          * @param {!Point} n - Point object.
          * @return {Point} this (for chaining)
          */
      Point.prototype.sub = function (n) {
        this.x -= n.x;
        this.y -= n.y;
        return this; // for chaining
      };

      /**
          * Multiply this Point instance by a scalar. (component-wise)
          * @param {number} s   scalar.
          * @return {Point} this (for chaining)
          */
      Point.prototype.mul = function (s) {
        this.x *= s;
        this.y *= s;
        return this; // for chaining
      };

      /**
          * Return the distance of this Point instance from the origo.
          * @return {number} distance
          */
      Point.prototype.length = function () {
        return Math.sqrt(this.x * this.x + this.y * this.y);
      };

      /**
          * Normalize this Point instance (as a vector).
          * @return {number} The original distance of this instance from the origo.
          */
      Point.prototype.normalize = function () {
        var len = this.length();
        this.x /= len;
        this.y /= len;
        return len;
      };

      /**
          * Test this Point object with another for equality.
          * @param {!XY} p - any "Point like" object with {x,y}
          * @return {boolean} <code>true</code> if same x and y coordinates, <code>false</code> otherwise.
          */
      Point.prototype.equals = function (p) {
        return this.x === p.x && this.y === p.y;
      };


      // -----------------------------------------------------Point ("static" methods)

      /**
       * Negate a point component-wise and return the result as a new Point object.
       * @param {!XY} p - any "Point like" object with {x,y}
       * @return {Point} the resulting Point object.
       */
      Point.negate = function (p) {
        return new Point(-p.x, -p.y);
      };

      /**
          * Add two points component-wise and return the result as a new Point object.
          * @param {!XY} a - any "Point like" object with {x,y}
          * @param {!XY} b - any "Point like" object with {x,y}
          * @return {Point} the resulting Point object.
          */
      Point.add = function (a, b) {
        return new Point(a.x + b.x, a.y + b.y);
      };

      /**
          * Subtract two points component-wise and return the result as a new Point object.
          * @param {!XY} a - any "Point like" object with {x,y}
          * @param {!XY} b - any "Point like" object with {x,y}
          * @return {Point} the resulting Point object.
          */
      Point.sub = function (a, b) {
        return new Point(a.x - b.x, a.y - b.y);
      };

      /**
          * Multiply a point by a scalar and return the result as a new Point object.
          * @param {number} s - the scalar
          * @param {!XY} p - any "Point like" object with {x,y}
          * @return {Point} the resulting Point object.
          */
      Point.mul = function (s, p) {
        return new Point(s * p.x, s * p.y);
      };

      /**
          * Perform the cross product on either two points (this produces a scalar)
          * or a point and a scalar (this produces a point).
          * This function requires two parameters, either may be a Point object or a
          * number.
          * @param  {XY|number} a - Point object or scalar.
          * @param  {XY|number} b - Point object or scalar.
          * @return {Point|number} a Point object or a number, depending on the parameters.
          */
      Point.cross = function (a, b) {
        if (typeof a === 'number') {
          if (typeof b === 'number') {
            return a * b;
          } else {
            return new Point(-a * b.y, a * b.x);
          }
        } else {
          if (typeof b === 'number') {
            return new Point(b * a.y, -b * a.x);
          } else {
            return a.x * b.y - a.y * b.x;
          }
        }
      };


      // -----------------------------------------------------------------"Point-Like"
      /*
       * The following functions operate on "Point" or any "Point like" object 
       * with {x,y} (duck typing).
       */

      Point.toString = xy.toString;
      Point.compare = xy.compare;
      Point.cmp = xy.compare; // backward compatibility
      Point.equals = xy.equals;

      /**
                                 * Peform the dot product on two vectors.
                                 * @public
                                 * @param {!XY} a - any "Point like" object with {x,y}
                                 * @param {!XY} b - any "Point like" object with {x,y}
                                 * @return {number} The dot product
                                 */
      Point.dot = function (a, b) {
        return a.x * b.x + a.y * b.y;
      };


      // ---------------------------------------------------------Exports (public API)

      module.exports = Point;

    }, { "./xy": 11 }], 5: [function (require, module, exports) {
      /*
                                                                  * Poly2Tri Copyright (c) 2009-2014, Poly2Tri Contributors
                                                                  * http://code.google.com/p/poly2tri/
                                                                  * 
                                                                  * poly2tri.js (JavaScript port) (c) 2009-2014, Poly2Tri Contributors
                                                                  * https://github.com/r3mi/poly2tri.js
                                                                  * 
                                                                  * All rights reserved.
                                                                  * 
                                                                  * Distributed under the 3-clause BSD License, see LICENSE.txt
                                                                  */

      "use strict";

      /*
                     * Class added in the JavaScript version (was not present in the c++ version)
                     */

      var xy = require('./xy');

      /**
                                 * Custom exception class to indicate invalid Point values
                                 * @constructor
                                 * @public
                                 * @extends Error
                                 * @struct
                                 * @param {string=} message - error message
                                 * @param {Array.<XY>=} points - invalid points
                                 */
      var PointError = function PointError(message, points) {
        this.name = "PointError";
        /**
                                   * Invalid points
                                   * @public
                                   * @type {Array.<XY>}
                                   */
        this.points = points = points || [];
        /**
                                              * Error message
                                              * @public
                                              * @type {string}
                                              */
        this.message = message || "Invalid Points!";
        for (var i = 0; i < points.length; i++) {
          this.message += " " + xy.toString(points[i]);
        }
      };
      PointError.prototype = new Error();
      PointError.prototype.constructor = PointError;


      module.exports = PointError;

    }, { "./xy": 11 }], 6: [function (require, module, exports) {
      (function (global) {
        /*
                           * Poly2Tri Copyright (c) 2009-2014, Poly2Tri Contributors
                           * http://code.google.com/p/poly2tri/
                           * 
                           * poly2tri.js (JavaScript port) (c) 2009-2014, Poly2Tri Contributors
                           * https://github.com/r3mi/poly2tri.js
                           *
                           * All rights reserved.
                           *
                           * Redistribution and use in source and binary forms, with or without modification,
                           * are permitted provided that the following conditions are met:
                           *
                           * * Redistributions of source code must retain the above copyright notice,
                           *   this list of conditions and the following disclaimer.
                           * * Redistributions in binary form must reproduce the above copyright notice,
                           *   this list of conditions and the following disclaimer in the documentation
                           *   and/or other materials provided with the distribution.
                           * * Neither the name of Poly2Tri nor the names of its contributors may be
                           *   used to endorse or promote products derived from this software without specific
                           *   prior written permission.
                           *
                           * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
                           * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
                           * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
                           * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
                           * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
                           * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
                           * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
                           * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
                           * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
                           * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
                           * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                           */

        "use strict";

        /**
                       * Public API for poly2tri.js
                       * @module poly2tri
                       */


        /**
                           * If you are not using a module system (e.g. CommonJS, RequireJS), you can access this library
                           * as a global variable <code>poly2tri</code> i.e. <code>window.poly2tri</code> in a browser.
                           * @name poly2tri
                           * @global
                           * @public
                           * @type {module:poly2tri}
                           */
        var previousPoly2tri = global.poly2tri;
        /**
                                                 * For Browser + &lt;script&gt; :
                                                 * reverts the {@linkcode poly2tri} global object to its previous value,
                                                 * and returns a reference to the instance called.
                                                 *
                                                 * @example
                                                 *              var p = poly2tri.noConflict();
                                                 * @public
                                                 * @return {module:poly2tri} instance called
                                                 */
        // (this feature is not automatically provided by browserify).
        exports.noConflict = function () {
          global.poly2tri = previousPoly2tri;
          return exports;
        };

        /**
            * poly2tri library version
            * @public
            * @const {string}
            */
        exports.VERSION = require('../dist/version.json').version;

        /**
                                                                    * Exports the {@linkcode PointError} class.
                                                                    * @public
                                                                    * @typedef {PointError} module:poly2tri.PointError
                                                                    * @function
                                                                    */
        exports.PointError = require('./pointerror');
        /**
                                                       * Exports the {@linkcode Point} class.
                                                       * @public
                                                       * @typedef {Point} module:poly2tri.Point
                                                       * @function
                                                       */
        exports.Point = require('./point');
        /**
                                             * Exports the {@linkcode Triangle} class.
                                             * @public
                                             * @typedef {Triangle} module:poly2tri.Triangle
                                             * @function
                                             */
        exports.Triangle = require('./triangle');
        /**
                                                   * Exports the {@linkcode SweepContext} class.
                                                   * @public
                                                   * @typedef {SweepContext} module:poly2tri.SweepContext
                                                   * @function
                                                   */
        exports.SweepContext = require('./sweepcontext');


        // Backward compatibility
        var sweep = require('./sweep');
        /**
                                         * @function
                                         * @deprecated use {@linkcode SweepContext#triangulate} instead
                                         */
        exports.triangulate = sweep.triangulate;
        /**
                                                  * @deprecated use {@linkcode SweepContext#triangulate} instead
                                                  * @property {function} Triangulate - use {@linkcode SweepContext#triangulate} instead
                                                  */
        exports.sweep = { Triangulate: sweep.triangulate };

      }).call(this, typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {});
    }, { "../dist/version.json": 1, "./point": 4, "./pointerror": 5, "./sweep": 7, "./sweepcontext": 8, "./triangle": 9 }], 7: [function (require, module, exports) {
      /*
                                                                                                                                                                      * Poly2Tri Copyright (c) 2009-2014, Poly2Tri Contributors
                                                                                                                                                                      * http://code.google.com/p/poly2tri/
                                                                                                                                                                      * 
                                                                                                                                                                      * poly2tri.js (JavaScript port) (c) 2009-2014, Poly2Tri Contributors
                                                                                                                                                                      * https://github.com/r3mi/poly2tri.js
                                                                                                                                                                      * 
                                                                                                                                                                      * All rights reserved.
                                                                                                                                                                      * 
                                                                                                                                                                      * Distributed under the 3-clause BSD License, see LICENSE.txt
                                                                                                                                                                      */

      /* jshint latedef:nofunc, maxcomplexity:9 */

      "use strict";

      /**
                     * This 'Sweep' module is present in order to keep this JavaScript version
                     * as close as possible to the reference C++ version, even though almost all
                     * functions could be declared as methods on the {@linkcode module:sweepcontext~SweepContext} object.
                     * @module
                     * @private
                     */

      /*
                         * Note
                         * ====
                         * the structure of this JavaScript version of poly2tri intentionally follows
                         * as closely as possible the structure of the reference C++ version, to make it 
                         * easier to keep the 2 versions in sync.
                         */

      var assert = require('./assert');
      var PointError = require('./pointerror');
      var Triangle = require('./triangle');
      var Node = require('./advancingfront').Node;


      // ------------------------------------------------------------------------utils

      var utils = require('./utils');

      /** @const */
      var EPSILON = utils.EPSILON;

      /** @const */
      var Orientation = utils.Orientation;
      /** @const */
      var orient2d = utils.orient2d;
      /** @const */
      var inScanArea = utils.inScanArea;
      /** @const */
      var isAngleObtuse = utils.isAngleObtuse;


      // ------------------------------------------------------------------------Sweep

      /**
       * Triangulate the polygon with holes and Steiner points.
       * Do this AFTER you've added the polyline, holes, and Steiner points
       * @private
       * @param {!SweepContext} tcx - SweepContext object
       */
      function triangulate(tcx) {
        tcx.initTriangulation();
        tcx.createAdvancingFront();
        // Sweep points; build mesh
        sweepPoints(tcx);
        // Clean up
        finalizationPolygon(tcx);
      }

      /**
         * Start sweeping the Y-sorted point set from bottom to top
         * @param {!SweepContext} tcx - SweepContext object
         */
      function sweepPoints(tcx) {
        var i,len = tcx.pointCount();
        for (i = 1; i < len; ++i) {
          var point = tcx.getPoint(i);
          var node = pointEvent(tcx, point);
          var edges = point._p2t_edge_list;
          for (var j = 0; edges && j < edges.length; ++j) {
            edgeEventByEdge(tcx, edges[j], node);
          }
        }
      }

      /**
         * @param {!SweepContext} tcx - SweepContext object
         */
      function finalizationPolygon(tcx) {
        // Get an Internal triangle to start with
        var t = tcx.front().head().next.triangle;
        var p = tcx.front().head().next.point;
        while (!t.getConstrainedEdgeCW(p)) {
          t = t.neighborCCW(p);
        }

        // Collect interior triangles constrained by edges
        tcx.meshClean(t);
      }

      /**
         * Find closes node to the left of the new point and
         * create a new triangle. If needed new holes and basins
         * will be filled to.
         * @param {!SweepContext} tcx - SweepContext object
         * @param {!XY} point   Point
         */
      function pointEvent(tcx, point) {
        var node = tcx.locateNode(point);
        var new_node = newFrontTriangle(tcx, point, node);

        // Only need to check +epsilon since point never have smaller
        // x value than node due to how we fetch nodes from the front
        if (point.x <= node.point.x + EPSILON) {
          fill(tcx, node);
        }

        //tcx.AddNode(new_node);

        fillAdvancingFront(tcx, new_node);
        return new_node;
      }

      function edgeEventByEdge(tcx, edge, node) {
        tcx.edge_event.constrained_edge = edge;
        tcx.edge_event.right = edge.p.x > edge.q.x;

        if (isEdgeSideOfTriangle(node.triangle, edge.p, edge.q)) {
          return;
        }

        // For now we will do all needed filling
        // TODO: integrate with flip process might give some better performance
        //       but for now this avoid the issue with cases that needs both flips and fills
        fillEdgeEvent(tcx, edge, node);
        edgeEventByPoints(tcx, edge.p, edge.q, node.triangle, edge.q);
      }

      function edgeEventByPoints(tcx, ep, eq, triangle, point) {
        if (isEdgeSideOfTriangle(triangle, ep, eq)) {
          return;
        }

        var p1 = triangle.pointCCW(point);
        var o1 = orient2d(eq, p1, ep);
        if (o1 === Orientation.COLLINEAR) {
          // TODO integrate here changes from C++ version
          // (C++ repo revision 09880a869095 dated March 8, 2011)
          throw new PointError('poly2tri EdgeEvent: Collinear not supported!', [eq, p1, ep]);
        }

        var p2 = triangle.pointCW(point);
        var o2 = orient2d(eq, p2, ep);
        if (o2 === Orientation.COLLINEAR) {
          // TODO integrate here changes from C++ version
          // (C++ repo revision 09880a869095 dated March 8, 2011)
          throw new PointError('poly2tri EdgeEvent: Collinear not supported!', [eq, p2, ep]);
        }

        if (o1 === o2) {
          // Need to decide if we are rotating CW or CCW to get to a triangle
          // that will cross edge
          if (o1 === Orientation.CW) {
            triangle = triangle.neighborCCW(point);
          } else {
            triangle = triangle.neighborCW(point);
          }
          edgeEventByPoints(tcx, ep, eq, triangle, point);
        } else {
          // This triangle crosses constraint so lets flippin start!
          flipEdgeEvent(tcx, ep, eq, triangle, point);
        }
      }

      function isEdgeSideOfTriangle(triangle, ep, eq) {
        var index = triangle.edgeIndex(ep, eq);
        if (index !== -1) {
          triangle.markConstrainedEdgeByIndex(index);
          var t = triangle.getNeighbor(index);
          if (t) {
            t.markConstrainedEdgeByPoints(ep, eq);
          }
          return true;
        }
        return false;
      }

      /**
         * Creates a new front triangle and legalize it
         * @param {!SweepContext} tcx - SweepContext object
         */
      function newFrontTriangle(tcx, point, node) {
        var triangle = new Triangle(point, node.point, node.next.point);

        triangle.markNeighbor(node.triangle);
        tcx.addToMap(triangle);

        var new_node = new Node(point);
        new_node.next = node.next;
        new_node.prev = node;
        node.next.prev = new_node;
        node.next = new_node;

        if (!legalize(tcx, triangle)) {
          tcx.mapTriangleToNodes(triangle);
        }

        return new_node;
      }

      /**
         * Adds a triangle to the advancing front to fill a hole.
         * @param {!SweepContext} tcx - SweepContext object
         * @param node - middle node, that is the bottom of the hole
         */
      function fill(tcx, node) {
        var triangle = new Triangle(node.prev.point, node.point, node.next.point);

        // TODO: should copy the constrained_edge value from neighbor triangles
        //       for now constrained_edge values are copied during the legalize
        triangle.markNeighbor(node.prev.triangle);
        triangle.markNeighbor(node.triangle);

        tcx.addToMap(triangle);

        // Update the advancing front
        node.prev.next = node.next;
        node.next.prev = node.prev;


        // If it was legalized the triangle has already been mapped
        if (!legalize(tcx, triangle)) {
          tcx.mapTriangleToNodes(triangle);
        }

        //tcx.removeNode(node);
      }

      /**
         * Fills holes in the Advancing Front
         * @param {!SweepContext} tcx - SweepContext object
         */
      function fillAdvancingFront(tcx, n) {
        // Fill right holes
        var node = n.next;
        while (node.next) {
          // TODO integrate here changes from C++ version
          // (C++ repo revision acf81f1f1764 dated April 7, 2012)
          if (isAngleObtuse(node.point, node.next.point, node.prev.point)) {
            break;
          }
          fill(tcx, node);
          node = node.next;
        }

        // Fill left holes
        node = n.prev;
        while (node.prev) {
          // TODO integrate here changes from C++ version
          // (C++ repo revision acf81f1f1764 dated April 7, 2012)
          if (isAngleObtuse(node.point, node.next.point, node.prev.point)) {
            break;
          }
          fill(tcx, node);
          node = node.prev;
        }

        // Fill right basins
        if (n.next && n.next.next) {
          if (isBasinAngleRight(n)) {
            fillBasin(tcx, n);
          }
        }
      }

      /**
         * The basin angle is decided against the horizontal line [1,0].
         * @param {Node} node
         * @return {boolean} true if angle < 3*π/4
         */
      function isBasinAngleRight(node) {
        var ax = node.point.x - node.next.next.point.x;
        var ay = node.point.y - node.next.next.point.y;
        assert(ay >= 0, "unordered y");
        return ax >= 0 || Math.abs(ax) < ay;
      }

      /**
         * Returns true if triangle was legalized
         * @param {!SweepContext} tcx - SweepContext object
         * @return {boolean}
         */
      function legalize(tcx, t) {
        // To legalize a triangle we start by finding if any of the three edges
        // violate the Delaunay condition
        for (var i = 0; i < 3; ++i) {
          if (t.delaunay_edge[i]) {
            continue;
          }
          var ot = t.getNeighbor(i);
          if (ot) {
            var p = t.getPoint(i);
            var op = ot.oppositePoint(t, p);
            var oi = ot.index(op);

            // If this is a Constrained Edge or a Delaunay Edge(only during recursive legalization)
            // then we should not try to legalize
            if (ot.constrained_edge[oi] || ot.delaunay_edge[oi]) {
              t.constrained_edge[i] = ot.constrained_edge[oi];
              continue;
            }

            var inside = inCircle(p, t.pointCCW(p), t.pointCW(p), op);
            if (inside) {
              // Lets mark this shared edge as Delaunay
              t.delaunay_edge[i] = true;
              ot.delaunay_edge[oi] = true;

              // Lets rotate shared edge one vertex CW to legalize it
              rotateTrianglePair(t, p, ot, op);

              // We now got one valid Delaunay Edge shared by two triangles
              // This gives us 4 new edges to check for Delaunay

              // Make sure that triangle to node mapping is done only one time for a specific triangle
              var not_legalized = !legalize(tcx, t);
              if (not_legalized) {
                tcx.mapTriangleToNodes(t);
              }

              not_legalized = !legalize(tcx, ot);
              if (not_legalized) {
                tcx.mapTriangleToNodes(ot);
              }
              // Reset the Delaunay edges, since they only are valid Delaunay edges
              // until we add a new triangle or point.
              // XXX: need to think about this. Can these edges be tried after we
              //      return to previous recursive level?
              t.delaunay_edge[i] = false;
              ot.delaunay_edge[oi] = false;

              // If triangle have been legalized no need to check the other edges since
              // the recursive legalization will handles those so we can end here.
              return true;
            }
          }
        }
        return false;
      }

      /**
         * <b>Requirement</b>:<br>
         * 1. a,b and c form a triangle.<br>
         * 2. a and d is know to be on opposite side of bc<br>
         * <pre>
         *                a
         *                +
         *               / \
         *              /   \
         *            b/     \c
         *            +-------+
         *           /    d    \
         *          /           \
         * </pre>
         * <b>Fact</b>: d has to be in area B to have a chance to be inside the circle formed by
         *  a,b and c<br>
         *  d is outside B if orient2d(a,b,d) or orient2d(c,a,d) is CW<br>
         *  This preknowledge gives us a way to optimize the incircle test
         * @param pa - triangle point, opposite d
         * @param pb - triangle point
         * @param pc - triangle point
         * @param pd - point opposite a
         * @return {boolean} true if d is inside circle, false if on circle edge
         */
      function inCircle(pa, pb, pc, pd) {
        var adx = pa.x - pd.x;
        var ady = pa.y - pd.y;
        var bdx = pb.x - pd.x;
        var bdy = pb.y - pd.y;

        var adxbdy = adx * bdy;
        var bdxady = bdx * ady;
        var oabd = adxbdy - bdxady;
        if (oabd <= 0) {
          return false;
        }

        var cdx = pc.x - pd.x;
        var cdy = pc.y - pd.y;

        var cdxady = cdx * ady;
        var adxcdy = adx * cdy;
        var ocad = cdxady - adxcdy;
        if (ocad <= 0) {
          return false;
        }

        var bdxcdy = bdx * cdy;
        var cdxbdy = cdx * bdy;

        var alift = adx * adx + ady * ady;
        var blift = bdx * bdx + bdy * bdy;
        var clift = cdx * cdx + cdy * cdy;

        var det = alift * (bdxcdy - cdxbdy) + blift * ocad + clift * oabd;
        return det > 0;
      }

      /**
         * Rotates a triangle pair one vertex CW
         *<pre>
         *       n2                    n2
         *  P +-----+             P +-----+
         *    | t  /|               |\  t |
         *    |   / |               | \   |
         *  n1|  /  |n3           n1|  \  |n3
         *    | /   |    after CW   |   \ |
         *    |/ oT |               | oT \|
         *    +-----+ oP            +-----+
         *       n4                    n4
         * </pre>
         */
      function rotateTrianglePair(t, p, ot, op) {
        var n1, n2, n3, n4;
        n1 = t.neighborCCW(p);
        n2 = t.neighborCW(p);
        n3 = ot.neighborCCW(op);
        n4 = ot.neighborCW(op);

        var ce1, ce2, ce3, ce4;
        ce1 = t.getConstrainedEdgeCCW(p);
        ce2 = t.getConstrainedEdgeCW(p);
        ce3 = ot.getConstrainedEdgeCCW(op);
        ce4 = ot.getConstrainedEdgeCW(op);

        var de1, de2, de3, de4;
        de1 = t.getDelaunayEdgeCCW(p);
        de2 = t.getDelaunayEdgeCW(p);
        de3 = ot.getDelaunayEdgeCCW(op);
        de4 = ot.getDelaunayEdgeCW(op);

        t.legalize(p, op);
        ot.legalize(op, p);

        // Remap delaunay_edge
        ot.setDelaunayEdgeCCW(p, de1);
        t.setDelaunayEdgeCW(p, de2);
        t.setDelaunayEdgeCCW(op, de3);
        ot.setDelaunayEdgeCW(op, de4);

        // Remap constrained_edge
        ot.setConstrainedEdgeCCW(p, ce1);
        t.setConstrainedEdgeCW(p, ce2);
        t.setConstrainedEdgeCCW(op, ce3);
        ot.setConstrainedEdgeCW(op, ce4);

        // Remap neighbors
        // XXX: might optimize the markNeighbor by keeping track of
        //      what side should be assigned to what neighbor after the
        //      rotation. Now mark neighbor does lots of testing to find
        //      the right side.
        t.clearNeighbors();
        ot.clearNeighbors();
        if (n1) {
          ot.markNeighbor(n1);
        }
        if (n2) {
          t.markNeighbor(n2);
        }
        if (n3) {
          t.markNeighbor(n3);
        }
        if (n4) {
          ot.markNeighbor(n4);
        }
        t.markNeighbor(ot);
      }

      /**
         * Fills a basin that has formed on the Advancing Front to the right
         * of given node.<br>
         * First we decide a left,bottom and right node that forms the
         * boundaries of the basin. Then we do a reqursive fill.
         *
         * @param {!SweepContext} tcx - SweepContext object
         * @param node - starting node, this or next node will be left node
         */
      function fillBasin(tcx, node) {
        if (orient2d(node.point, node.next.point, node.next.next.point) === Orientation.CCW) {
          tcx.basin.left_node = node.next.next;
        } else {
          tcx.basin.left_node = node.next;
        }

        // Find the bottom and right node
        tcx.basin.bottom_node = tcx.basin.left_node;
        while (tcx.basin.bottom_node.next && tcx.basin.bottom_node.point.y >= tcx.basin.bottom_node.next.point.y) {
          tcx.basin.bottom_node = tcx.basin.bottom_node.next;
        }
        if (tcx.basin.bottom_node === tcx.basin.left_node) {
          // No valid basin
          return;
        }

        tcx.basin.right_node = tcx.basin.bottom_node;
        while (tcx.basin.right_node.next && tcx.basin.right_node.point.y < tcx.basin.right_node.next.point.y) {
          tcx.basin.right_node = tcx.basin.right_node.next;
        }
        if (tcx.basin.right_node === tcx.basin.bottom_node) {
          // No valid basins
          return;
        }

        tcx.basin.width = tcx.basin.right_node.point.x - tcx.basin.left_node.point.x;
        tcx.basin.left_highest = tcx.basin.left_node.point.y > tcx.basin.right_node.point.y;

        fillBasinReq(tcx, tcx.basin.bottom_node);
      }

      /**
         * Recursive algorithm to fill a Basin with triangles
         *
         * @param {!SweepContext} tcx - SweepContext object
         * @param node - bottom_node
         */
      function fillBasinReq(tcx, node) {
        // if shallow stop filling
        if (isShallow(tcx, node)) {
          return;
        }

        fill(tcx, node);

        var o;
        if (node.prev === tcx.basin.left_node && node.next === tcx.basin.right_node) {
          return;
        } else if (node.prev === tcx.basin.left_node) {
          o = orient2d(node.point, node.next.point, node.next.next.point);
          if (o === Orientation.CW) {
            return;
          }
          node = node.next;
        } else if (node.next === tcx.basin.right_node) {
          o = orient2d(node.point, node.prev.point, node.prev.prev.point);
          if (o === Orientation.CCW) {
            return;
          }
          node = node.prev;
        } else {
          // Continue with the neighbor node with lowest Y value
          if (node.prev.point.y < node.next.point.y) {
            node = node.prev;
          } else {
            node = node.next;
          }
        }

        fillBasinReq(tcx, node);
      }

      function isShallow(tcx, node) {
        var height;
        if (tcx.basin.left_highest) {
          height = tcx.basin.left_node.point.y - node.point.y;
        } else {
          height = tcx.basin.right_node.point.y - node.point.y;
        }

        // if shallow stop filling
        if (tcx.basin.width > height) {
          return true;
        }
        return false;
      }

      function fillEdgeEvent(tcx, edge, node) {
        if (tcx.edge_event.right) {
          fillRightAboveEdgeEvent(tcx, edge, node);
        } else {
          fillLeftAboveEdgeEvent(tcx, edge, node);
        }
      }

      function fillRightAboveEdgeEvent(tcx, edge, node) {
        while (node.next.point.x < edge.p.x) {
          // Check if next node is below the edge
          if (orient2d(edge.q, node.next.point, edge.p) === Orientation.CCW) {
            fillRightBelowEdgeEvent(tcx, edge, node);
          } else {
            node = node.next;
          }
        }
      }

      function fillRightBelowEdgeEvent(tcx, edge, node) {
        if (node.point.x < edge.p.x) {
          if (orient2d(node.point, node.next.point, node.next.next.point) === Orientation.CCW) {
            // Concave
            fillRightConcaveEdgeEvent(tcx, edge, node);
          } else {
            // Convex
            fillRightConvexEdgeEvent(tcx, edge, node);
            // Retry this one
            fillRightBelowEdgeEvent(tcx, edge, node);
          }
        }
      }

      function fillRightConcaveEdgeEvent(tcx, edge, node) {
        fill(tcx, node.next);
        if (node.next.point !== edge.p) {
          // Next above or below edge?
          if (orient2d(edge.q, node.next.point, edge.p) === Orientation.CCW) {
            // Below
            if (orient2d(node.point, node.next.point, node.next.next.point) === Orientation.CCW) {
              // Next is concave
              fillRightConcaveEdgeEvent(tcx, edge, node);
            } else {
              // Next is convex
              /* jshint noempty:false */
            }
          }
        }
      }

      function fillRightConvexEdgeEvent(tcx, edge, node) {
        // Next concave or convex?
        if (orient2d(node.next.point, node.next.next.point, node.next.next.next.point) === Orientation.CCW) {
          // Concave
          fillRightConcaveEdgeEvent(tcx, edge, node.next);
        } else {
          // Convex
          // Next above or below edge?
          if (orient2d(edge.q, node.next.next.point, edge.p) === Orientation.CCW) {
            // Below
            fillRightConvexEdgeEvent(tcx, edge, node.next);
          } else {
            // Above
            /* jshint noempty:false */
          }
        }
      }

      function fillLeftAboveEdgeEvent(tcx, edge, node) {
        while (node.prev.point.x > edge.p.x) {
          // Check if next node is below the edge
          if (orient2d(edge.q, node.prev.point, edge.p) === Orientation.CW) {
            fillLeftBelowEdgeEvent(tcx, edge, node);
          } else {
            node = node.prev;
          }
        }
      }

      function fillLeftBelowEdgeEvent(tcx, edge, node) {
        if (node.point.x > edge.p.x) {
          if (orient2d(node.point, node.prev.point, node.prev.prev.point) === Orientation.CW) {
            // Concave
            fillLeftConcaveEdgeEvent(tcx, edge, node);
          } else {
            // Convex
            fillLeftConvexEdgeEvent(tcx, edge, node);
            // Retry this one
            fillLeftBelowEdgeEvent(tcx, edge, node);
          }
        }
      }

      function fillLeftConvexEdgeEvent(tcx, edge, node) {
        // Next concave or convex?
        if (orient2d(node.prev.point, node.prev.prev.point, node.prev.prev.prev.point) === Orientation.CW) {
          // Concave
          fillLeftConcaveEdgeEvent(tcx, edge, node.prev);
        } else {
          // Convex
          // Next above or below edge?
          if (orient2d(edge.q, node.prev.prev.point, edge.p) === Orientation.CW) {
            // Below
            fillLeftConvexEdgeEvent(tcx, edge, node.prev);
          } else {
            // Above
            /* jshint noempty:false */
          }
        }
      }

      function fillLeftConcaveEdgeEvent(tcx, edge, node) {
        fill(tcx, node.prev);
        if (node.prev.point !== edge.p) {
          // Next above or below edge?
          if (orient2d(edge.q, node.prev.point, edge.p) === Orientation.CW) {
            // Below
            if (orient2d(node.point, node.prev.point, node.prev.prev.point) === Orientation.CW) {
              // Next is concave
              fillLeftConcaveEdgeEvent(tcx, edge, node);
            } else {
              // Next is convex
              /* jshint noempty:false */
            }
          }
        }
      }

      function flipEdgeEvent(tcx, ep, eq, t, p) {
        var ot = t.neighborAcross(p);
        assert(ot, "FLIP failed due to missing triangle!");

        var op = ot.oppositePoint(t, p);

        // Additional check from Java version (see issue #88)
        if (t.getConstrainedEdgeAcross(p)) {
          var index = t.index(p);
          throw new PointError("poly2tri Intersecting Constraints",
          [p, op, t.getPoint((index + 1) % 3), t.getPoint((index + 2) % 3)]);
        }

        if (inScanArea(p, t.pointCCW(p), t.pointCW(p), op)) {
          // Lets rotate shared edge one vertex CW
          rotateTrianglePair(t, p, ot, op);
          tcx.mapTriangleToNodes(t);
          tcx.mapTriangleToNodes(ot);

          // XXX: in the original C++ code for the next 2 lines, we are
          // comparing point values (and not pointers). In this JavaScript
          // code, we are comparing point references (pointers). This works
          // because we can't have 2 different points with the same values.
          // But to be really equivalent, we should use "Point.equals" here.
          if (p === eq && op === ep) {
            if (eq === tcx.edge_event.constrained_edge.q && ep === tcx.edge_event.constrained_edge.p) {
              t.markConstrainedEdgeByPoints(ep, eq);
              ot.markConstrainedEdgeByPoints(ep, eq);
              legalize(tcx, t);
              legalize(tcx, ot);
            } else {
              // XXX: I think one of the triangles should be legalized here?
              /* jshint noempty:false */
            }
          } else {
            var o = orient2d(eq, op, ep);
            t = nextFlipTriangle(tcx, o, t, ot, p, op);
            flipEdgeEvent(tcx, ep, eq, t, p);
          }
        } else {
          var newP = nextFlipPoint(ep, eq, ot, op);
          flipScanEdgeEvent(tcx, ep, eq, t, ot, newP);
          edgeEventByPoints(tcx, ep, eq, t, p);
        }
      }

      /**
         * After a flip we have two triangles and know that only one will still be
         * intersecting the edge. So decide which to contiune with and legalize the other
         *
         * @param {!SweepContext} tcx - SweepContext object
         * @param o - should be the result of an orient2d( eq, op, ep )
         * @param t - triangle 1
         * @param ot - triangle 2
         * @param p - a point shared by both triangles
         * @param op - another point shared by both triangles
         * @return returns the triangle still intersecting the edge
         */
      function nextFlipTriangle(tcx, o, t, ot, p, op) {
        var edge_index;
        if (o === Orientation.CCW) {
          // ot is not crossing edge after flip
          edge_index = ot.edgeIndex(p, op);
          ot.delaunay_edge[edge_index] = true;
          legalize(tcx, ot);
          ot.clearDelaunayEdges();
          return t;
        }

        // t is not crossing edge after flip
        edge_index = t.edgeIndex(p, op);

        t.delaunay_edge[edge_index] = true;
        legalize(tcx, t);
        t.clearDelaunayEdges();
        return ot;
      }

      /**
         * When we need to traverse from one triangle to the next we need
         * the point in current triangle that is the opposite point to the next
         * triangle.
         */
      function nextFlipPoint(ep, eq, ot, op) {
        var o2d = orient2d(eq, op, ep);
        if (o2d === Orientation.CW) {
          // Right
          return ot.pointCCW(op);
        } else if (o2d === Orientation.CCW) {
          // Left
          return ot.pointCW(op);
        } else {
          throw new PointError("poly2tri [Unsupported] nextFlipPoint: opposing point on constrained edge!", [eq, op, ep]);
        }
      }

      /**
         * Scan part of the FlipScan algorithm<br>
         * When a triangle pair isn't flippable we will scan for the next
         * point that is inside the flip triangle scan area. When found
         * we generate a new flipEdgeEvent
         *
         * @param {!SweepContext} tcx - SweepContext object
         * @param ep - last point on the edge we are traversing
         * @param eq - first point on the edge we are traversing
         * @param {!Triangle} flip_triangle - the current triangle sharing the point eq with edge
         * @param t
         * @param p
         */
      function flipScanEdgeEvent(tcx, ep, eq, flip_triangle, t, p) {
        var ot = t.neighborAcross(p);
        assert(ot, "FLIP failed due to missing triangle");

        var op = ot.oppositePoint(t, p);

        if (inScanArea(eq, flip_triangle.pointCCW(eq), flip_triangle.pointCW(eq), op)) {
          // flip with new edge op.eq
          flipEdgeEvent(tcx, eq, op, ot, op);
        } else {
          var newP = nextFlipPoint(ep, eq, ot, op);
          flipScanEdgeEvent(tcx, ep, eq, flip_triangle, ot, newP);
        }
      }


      // ----------------------------------------------------------------------Exports

      exports.triangulate = triangulate;

    }, { "./advancingfront": 2, "./assert": 3, "./pointerror": 5, "./triangle": 9, "./utils": 10 }], 8: [function (require, module, exports) {
      /*
                                                                                                                                               * Poly2Tri Copyright (c) 2009-2014, Poly2Tri Contributors
                                                                                                                                               * http://code.google.com/p/poly2tri/
                                                                                                                                               * 
                                                                                                                                               * poly2tri.js (JavaScript port) (c) 2009-2014, Poly2Tri Contributors
                                                                                                                                               * https://github.com/r3mi/poly2tri.js
                                                                                                                                               * 
                                                                                                                                               * All rights reserved.
                                                                                                                                               * 
                                                                                                                                               * Distributed under the 3-clause BSD License, see LICENSE.txt
                                                                                                                                               */

      /* jshint maxcomplexity:6 */

      "use strict";


      /*
                     * Note
                     * ====
                     * the structure of this JavaScript version of poly2tri intentionally follows
                     * as closely as possible the structure of the reference C++ version, to make it 
                     * easier to keep the 2 versions in sync.
                     */

      var PointError = require('./pointerror');
      var Point = require('./point');
      var Triangle = require('./triangle');
      var sweep = require('./sweep');
      var AdvancingFront = require('./advancingfront');
      var Node = AdvancingFront.Node;


      // ------------------------------------------------------------------------utils

      /**
       * Initial triangle factor, seed triangle will extend 30% of
       * PointSet width to both left and right.
       * @private
       * @const
       */
      var kAlpha = 0.3;


      // -------------------------------------------------------------------------Edge
      /**
       * Represents a simple polygon's edge
       * @constructor
       * @struct
       * @private
       * @param {Point} p1
       * @param {Point} p2
       * @throw {PointError} if p1 is same as p2
       */
      var Edge = function Edge(p1, p2) {
        this.p = p1;
        this.q = p2;

        if (p1.y > p2.y) {
          this.q = p1;
          this.p = p2;
        } else if (p1.y === p2.y) {
          if (p1.x > p2.x) {
            this.q = p1;
            this.p = p2;
          } else if (p1.x === p2.x) {
            throw new PointError('poly2tri Invalid Edge constructor: repeated points!', [p1]);
          }
        }

        if (!this.q._p2t_edge_list) {
          this.q._p2t_edge_list = [];
        }
        this.q._p2t_edge_list.push(this);
      };


      // ------------------------------------------------------------------------Basin
      /**
       * @constructor
       * @struct
       * @private
       */
      var Basin = function Basin() {
        /** @type {Node} */
        this.left_node = null;
        /** @type {Node} */
        this.bottom_node = null;
        /** @type {Node} */
        this.right_node = null;
        /** @type {number} */
        this.width = 0.0;
        /** @type {boolean} */
        this.left_highest = false;
      };

      Basin.prototype.clear = function () {
        this.left_node = null;
        this.bottom_node = null;
        this.right_node = null;
        this.width = 0.0;
        this.left_highest = false;
      };

      // --------------------------------------------------------------------EdgeEvent
      /**
       * @constructor
       * @struct
       * @private
       */
      var EdgeEvent = function EdgeEvent() {
        /** @type {Edge} */
        this.constrained_edge = null;
        /** @type {boolean} */
        this.right = false;
      };

      // ----------------------------------------------------SweepContext (public API)
      /**
       * SweepContext constructor option
       * @typedef {Object} SweepContextOptions
       * @property {boolean=} cloneArrays - if <code>true</code>, do a shallow copy of the Array parameters
       *                  (contour, holes). Points inside arrays are never copied.
       *                  Default is <code>false</code> : keep a reference to the array arguments,
       *                  who will be modified in place.
       */
      /**
           * Constructor for the triangulation context.
           * It accepts a simple polyline (with non repeating points), 
           * which defines the constrained edges.
           *
           * @example
           *          var contour = [
           *              new poly2tri.Point(100, 100),
           *              new poly2tri.Point(100, 300),
           *              new poly2tri.Point(300, 300),
           *              new poly2tri.Point(300, 100)
           *          ];
           *          var swctx = new poly2tri.SweepContext(contour, {cloneArrays: true});
           * @example
           *          var contour = [{x:100, y:100}, {x:100, y:300}, {x:300, y:300}, {x:300, y:100}];
           *          var swctx = new poly2tri.SweepContext(contour, {cloneArrays: true});
           * @constructor
           * @public
           * @struct
           * @param {Array.<XY>} contour - array of point objects. The points can be either {@linkcode Point} instances,
           *          or any "Point like" custom class with <code>{x, y}</code> attributes.
           * @param {SweepContextOptions=} options - constructor options
           */
      var SweepContext = function SweepContext(contour, options) {
        options = options || {};
        this.triangles_ = [];
        this.map_ = [];
        this.points_ = options.cloneArrays ? contour.slice(0) : contour;
        this.edge_list = [];

        // Bounding box of all points. Computed at the start of the triangulation, 
        // it is stored in case it is needed by the caller.
        this.pmin_ = this.pmax_ = null;

        /**
                                         * Advancing front
                                         * @private
                                         * @type {AdvancingFront}
                                         */
        this.front_ = null;

        /**
                             * head point used with advancing front
                             * @private
                             * @type {Point}
                             */
        this.head_ = null;

        /**
                            * tail point used with advancing front
                            * @private
                            * @type {Point}
                            */
        this.tail_ = null;

        /**
                            * @private
                            * @type {Node}
                            */
        this.af_head_ = null;
        /**
                               * @private
                               * @type {Node}
                               */
        this.af_middle_ = null;
        /**
                                 * @private
                                 * @type {Node}
                                 */
        this.af_tail_ = null;

        this.basin = new Basin();
        this.edge_event = new EdgeEvent();

        this.initEdges(this.points_);
      };


      /**
          * Add a hole to the constraints
          * @example
          *      var swctx = new poly2tri.SweepContext(contour);
          *      var hole = [
          *          new poly2tri.Point(200, 200),
          *          new poly2tri.Point(200, 250),
          *          new poly2tri.Point(250, 250)
          *      ];
          *      swctx.addHole(hole);
          * @example
          *      var swctx = new poly2tri.SweepContext(contour);
          *      swctx.addHole([{x:200, y:200}, {x:200, y:250}, {x:250, y:250}]);
          * @public
          * @param {Array.<XY>} polyline - array of "Point like" objects with {x,y}
          */
      SweepContext.prototype.addHole = function (polyline) {
        this.initEdges(polyline);
        var i,len = polyline.length;
        for (i = 0; i < len; i++) {
          this.points_.push(polyline[i]);
        }
        return this; // for chaining
      };

      /**
          * For backward compatibility
          * @function
          * @deprecated use {@linkcode SweepContext#addHole} instead
          */
      SweepContext.prototype.AddHole = SweepContext.prototype.addHole;


      /**
                                                                        * Add several holes to the constraints
                                                                        * @example
                                                                        *      var swctx = new poly2tri.SweepContext(contour);
                                                                        *      var holes = [
                                                                        *          [ new poly2tri.Point(200, 200), new poly2tri.Point(200, 250), new poly2tri.Point(250, 250) ],
                                                                        *          [ new poly2tri.Point(300, 300), new poly2tri.Point(300, 350), new poly2tri.Point(350, 350) ]
                                                                        *      ];
                                                                        *      swctx.addHoles(holes);
                                                                        * @example
                                                                        *      var swctx = new poly2tri.SweepContext(contour);
                                                                        *      var holes = [
                                                                        *          [{x:200, y:200}, {x:200, y:250}, {x:250, y:250}],
                                                                        *          [{x:300, y:300}, {x:300, y:350}, {x:350, y:350}]
                                                                        *      ];
                                                                        *      swctx.addHoles(holes);
                                                                        * @public
                                                                        * @param {Array.<Array.<XY>>} holes - array of array of "Point like" objects with {x,y}
                                                                        */
      // Method added in the JavaScript version (was not present in the c++ version)
      SweepContext.prototype.addHoles = function (holes) {
        var i,len = holes.length;
        for (i = 0; i < len; i++) {
          this.initEdges(holes[i]);
        }
        this.points_ = this.points_.concat.apply(this.points_, holes);
        return this; // for chaining
      };


      /**
          * Add a Steiner point to the constraints
          * @example
          *      var swctx = new poly2tri.SweepContext(contour);
          *      var point = new poly2tri.Point(150, 150);
          *      swctx.addPoint(point);
          * @example
          *      var swctx = new poly2tri.SweepContext(contour);
          *      swctx.addPoint({x:150, y:150});
          * @public
          * @param {XY} point - any "Point like" object with {x,y}
          */
      SweepContext.prototype.addPoint = function (point) {
        this.points_.push(point);
        return this; // for chaining
      };

      /**
          * For backward compatibility
          * @function
          * @deprecated use {@linkcode SweepContext#addPoint} instead
          */
      SweepContext.prototype.AddPoint = SweepContext.prototype.addPoint;


      /**
                                                                          * Add several Steiner points to the constraints
                                                                          * @example
                                                                          *      var swctx = new poly2tri.SweepContext(contour);
                                                                          *      var points = [
                                                                          *          new poly2tri.Point(150, 150),
                                                                          *          new poly2tri.Point(200, 250),
                                                                          *          new poly2tri.Point(250, 250)
                                                                          *      ];
                                                                          *      swctx.addPoints(points);
                                                                          * @example
                                                                          *      var swctx = new poly2tri.SweepContext(contour);
                                                                          *      swctx.addPoints([{x:150, y:150}, {x:200, y:250}, {x:250, y:250}]);
                                                                          * @public
                                                                          * @param {Array.<XY>} points - array of "Point like" object with {x,y}
                                                                          */
      // Method added in the JavaScript version (was not present in the c++ version)
      SweepContext.prototype.addPoints = function (points) {
        this.points_ = this.points_.concat(points);
        return this; // for chaining
      };


      /**
          * Triangulate the polygon with holes and Steiner points.
          * Do this AFTER you've added the polyline, holes, and Steiner points
          * @example
          *      var swctx = new poly2tri.SweepContext(contour);
          *      swctx.triangulate();
          *      var triangles = swctx.getTriangles();
          * @public
          */
      // Shortcut method for sweep.triangulate(SweepContext).
      // Method added in the JavaScript version (was not present in the c++ version)
      SweepContext.prototype.triangulate = function () {
        sweep.triangulate(this);
        return this; // for chaining
      };


      /**
          * Get the bounding box of the provided constraints (contour, holes and 
          * Steinter points). Warning : these values are not available if the triangulation 
          * has not been done yet.
          * @public
          * @returns {{min:Point,max:Point}} object with 'min' and 'max' Point
          */
      // Method added in the JavaScript version (was not present in the c++ version)
      SweepContext.prototype.getBoundingBox = function () {
        return { min: this.pmin_, max: this.pmax_ };
      };

      /**
          * Get result of triangulation.
          * The output triangles have vertices which are references
          * to the initial input points (not copies): any custom fields in the
          * initial points can be retrieved in the output triangles.
          * @example
          *      var swctx = new poly2tri.SweepContext(contour);
          *      swctx.triangulate();
          *      var triangles = swctx.getTriangles();
          * @example
          *      var contour = [{x:100, y:100, id:1}, {x:100, y:300, id:2}, {x:300, y:300, id:3}];
          *      var swctx = new poly2tri.SweepContext(contour);
          *      swctx.triangulate();
          *      var triangles = swctx.getTriangles();
          *      typeof triangles[0].getPoint(0).id
          *      // → "number"
          * @public
          * @returns {array<Triangle>}   array of triangles
          */
      SweepContext.prototype.getTriangles = function () {
        return this.triangles_;
      };

      /**
          * For backward compatibility
          * @function
          * @deprecated use {@linkcode SweepContext#getTriangles} instead
          */
      SweepContext.prototype.GetTriangles = SweepContext.prototype.getTriangles;


      // ---------------------------------------------------SweepContext (private API)

      /** @private */
      SweepContext.prototype.front = function () {
        return this.front_;
      };

      /** @private */
      SweepContext.prototype.pointCount = function () {
        return this.points_.length;
      };

      /** @private */
      SweepContext.prototype.head = function () {
        return this.head_;
      };

      /** @private */
      SweepContext.prototype.setHead = function (p1) {
        this.head_ = p1;
      };

      /** @private */
      SweepContext.prototype.tail = function () {
        return this.tail_;
      };

      /** @private */
      SweepContext.prototype.setTail = function (p1) {
        this.tail_ = p1;
      };

      /** @private */
      SweepContext.prototype.getMap = function () {
        return this.map_;
      };

      /** @private */
      SweepContext.prototype.initTriangulation = function () {
        var xmax = this.points_[0].x;
        var xmin = this.points_[0].x;
        var ymax = this.points_[0].y;
        var ymin = this.points_[0].y;

        // Calculate bounds
        var i,len = this.points_.length;
        for (i = 1; i < len; i++) {
          var p = this.points_[i];
          /* jshint expr:true */
          p.x > xmax && (xmax = p.x);
          p.x < xmin && (xmin = p.x);
          p.y > ymax && (ymax = p.y);
          p.y < ymin && (ymin = p.y);
        }
        this.pmin_ = new Point(xmin, ymin);
        this.pmax_ = new Point(xmax, ymax);

        var dx = kAlpha * (xmax - xmin);
        var dy = kAlpha * (ymax - ymin);
        this.head_ = new Point(xmax + dx, ymin - dy);
        this.tail_ = new Point(xmin - dx, ymin - dy);

        // Sort points along y-axis
        this.points_.sort(Point.compare);
      };

      /** @private */
      SweepContext.prototype.initEdges = function (polyline, isOpen) {
        var i,len = polyline.length,iEnd = isOpen ? polyline.length - 1 : polyline.length;
        for (i = 0; i < iEnd; ++i) {
          this.edge_list.push(new Edge(polyline[i], polyline[(i + 1) % len]));
        }
      };

      /** @private */
      SweepContext.prototype.getPoint = function (index) {
        return this.points_[index];
      };

      /** @private */
      SweepContext.prototype.addToMap = function (triangle) {
        this.map_.push(triangle);
      };

      /** @private */
      SweepContext.prototype.locateNode = function (point) {
        return this.front_.locateNode(point.x);
      };

      /** @private */
      SweepContext.prototype.createAdvancingFront = function () {
        var head;
        var middle;
        var tail;
        // Initial triangle
        var triangle = new Triangle(this.points_[0], this.tail_, this.head_);

        this.map_.push(triangle);

        head = new Node(triangle.getPoint(1), triangle);
        middle = new Node(triangle.getPoint(0), triangle);
        tail = new Node(triangle.getPoint(2));

        this.front_ = new AdvancingFront(head, tail);

        head.next = middle;
        middle.next = tail;
        middle.prev = head;
        tail.prev = middle;
      };

      /** @private */
      SweepContext.prototype.removeNode = function (node) {
        // do nothing
        /* jshint unused:false */
      };

      /** @private */
      SweepContext.prototype.mapTriangleToNodes = function (t) {
        for (var i = 0; i < 3; ++i) {
          if (!t.getNeighbor(i)) {
            var n = this.front_.locatePoint(t.pointCW(t.getPoint(i)));
            if (n) {
              n.triangle = t;
            }
          }
        }
      };

      /** @private */
      SweepContext.prototype.removeFromMap = function (triangle) {
        var i,map = this.map_,len = map.length;
        for (i = 0; i < len; i++) {
          if (map[i] === triangle) {
            map.splice(i, 1);
            break;
          }
        }
      };

      /**
          * Do a depth first traversal to collect triangles
          * @private
          * @param {Triangle} triangle start
          */
      SweepContext.prototype.meshClean = function (triangle) {
        // New implementation avoids recursive calls and use a loop instead.
        // Cf. issues # 57, 65 and 69.
        var triangles = [triangle],t,i;
        /* jshint boss:true */
        while (t = triangles.pop()) {
          if (!t.isInterior()) {
            t.setInterior(true);
            this.triangles_.push(t);
            for (i = 0; i < 3; i++) {
              if (!t.constrained_edge[i]) {
                triangles.push(t.getNeighbor(i));
              }
            }
          }
        }
      };

      // ----------------------------------------------------------------------Exports

      module.exports = SweepContext;

    }, { "./advancingfront": 2, "./point": 4, "./pointerror": 5, "./sweep": 7, "./triangle": 9 }], 9: [function (require, module, exports) {
      /*
                                                                                                                                             * Poly2Tri Copyright (c) 2009-2014, Poly2Tri Contributors
                                                                                                                                             * http://code.google.com/p/poly2tri/
                                                                                                                                             * 
                                                                                                                                             * poly2tri.js (JavaScript port) (c) 2009-2014, Poly2Tri Contributors
                                                                                                                                             * https://github.com/r3mi/poly2tri.js
                                                                                                                                             *
                                                                                                                                             * All rights reserved.
                                                                                                                                             * 
                                                                                                                                             * Distributed under the 3-clause BSD License, see LICENSE.txt
                                                                                                                                             */

      /* jshint maxcomplexity:10 */

      "use strict";


      /*
                     * Note
                     * ====
                     * the structure of this JavaScript version of poly2tri intentionally follows
                     * as closely as possible the structure of the reference C++ version, to make it 
                     * easier to keep the 2 versions in sync.
                     */

      var xy = require("./xy");


      // ---------------------------------------------------------------------Triangle
      /**
       * Triangle class.<br>
       * Triangle-based data structures are known to have better performance than
       * quad-edge structures.
       * See: J. Shewchuk, "Triangle: Engineering a 2D Quality Mesh Generator and
       * Delaunay Triangulator", "Triangulations in CGAL"
       *
       * @constructor
       * @struct
       * @param {!XY} pa  point object with {x,y}
       * @param {!XY} pb  point object with {x,y}
       * @param {!XY} pc  point object with {x,y}
       */
      var Triangle = function Triangle(a, b, c) {
        /**
                                                  * Triangle points
                                                  * @private
                                                  * @type {Array.<XY>}
                                                  */
        this.points_ = [a, b, c];

        /**
                                   * Neighbor list
                                   * @private
                                   * @type {Array.<Triangle>}
                                   */
        this.neighbors_ = [null, null, null];

        /**
                                               * Has this triangle been marked as an interior triangle?
                                               * @private
                                               * @type {boolean}
                                               */
        this.interior_ = false;

        /**
                                 * Flags to determine if an edge is a Constrained edge
                                 * @private
                                 * @type {Array.<boolean>}
                                 */
        this.constrained_edge = [false, false, false];

        /**
                                                        * Flags to determine if an edge is a Delauney edge
                                                        * @private
                                                        * @type {Array.<boolean>}
                                                        */
        this.delaunay_edge = [false, false, false];
      };

      var p2s = xy.toString;
      /**
                              * For pretty printing ex. <code>"[(5;42)(10;20)(21;30)]"</code>.
                              * @public
                              * @return {string}
                              */
      Triangle.prototype.toString = function () {
        return "[" + p2s(this.points_[0]) + p2s(this.points_[1]) + p2s(this.points_[2]) + "]";
      };

      /**
          * Get one vertice of the triangle.
          * The output triangles of a triangulation have vertices which are references
          * to the initial input points (not copies): any custom fields in the
          * initial points can be retrieved in the output triangles.
          * @example
          *      var contour = [{x:100, y:100, id:1}, {x:100, y:300, id:2}, {x:300, y:300, id:3}];
          *      var swctx = new poly2tri.SweepContext(contour);
          *      swctx.triangulate();
          *      var triangles = swctx.getTriangles();
          *      typeof triangles[0].getPoint(0).id
          *      // → "number"
          * @param {number} index - vertice index: 0, 1 or 2
          * @public
          * @returns {XY}
          */
      Triangle.prototype.getPoint = function (index) {
        return this.points_[index];
      };

      /**
          * For backward compatibility
          * @function
          * @deprecated use {@linkcode Triangle#getPoint} instead
          */
      Triangle.prototype.GetPoint = Triangle.prototype.getPoint;

      /**
                                                                  * Get all 3 vertices of the triangle as an array
                                                                  * @public
                                                                  * @return {Array.<XY>}
                                                                  */
      // Method added in the JavaScript version (was not present in the c++ version)
      Triangle.prototype.getPoints = function () {
        return this.points_;
      };

      /**
          * @private
          * @param {number} index
          * @returns {?Triangle}
          */
      Triangle.prototype.getNeighbor = function (index) {
        return this.neighbors_[index];
      };

      /**
          * Test if this Triangle contains the Point object given as parameter as one of its vertices.
          * Only point references are compared, not values.
          * @public
          * @param {XY} point - point object with {x,y}
          * @return {boolean} <code>True</code> if the Point object is of the Triangle's vertices,
          *         <code>false</code> otherwise.
          */
      Triangle.prototype.containsPoint = function (point) {
        var points = this.points_;
        // Here we are comparing point references, not values
        return point === points[0] || point === points[1] || point === points[2];
      };

      /**
          * Test if this Triangle contains the Edge object given as parameter as its
          * bounding edges. Only point references are compared, not values.
          * @private
          * @param {Edge} edge
          * @return {boolean} <code>True</code> if the Edge object is of the Triangle's bounding
          *         edges, <code>false</code> otherwise.
          */
      Triangle.prototype.containsEdge = function (edge) {
        return this.containsPoint(edge.p) && this.containsPoint(edge.q);
      };

      /**
          * Test if this Triangle contains the two Point objects given as parameters among its vertices.
          * Only point references are compared, not values.
          * @param {XY} p1 - point object with {x,y}
          * @param {XY} p2 - point object with {x,y}
          * @return {boolean}
          */
      Triangle.prototype.containsPoints = function (p1, p2) {
        return this.containsPoint(p1) && this.containsPoint(p2);
      };

      /**
          * Has this triangle been marked as an interior triangle?
          * @returns {boolean}
          */
      Triangle.prototype.isInterior = function () {
        return this.interior_;
      };

      /**
          * Mark this triangle as an interior triangle
          * @private
          * @param {boolean} interior
          * @returns {Triangle} this
          */
      Triangle.prototype.setInterior = function (interior) {
        this.interior_ = interior;
        return this;
      };

      /**
          * Update neighbor pointers.
          * @private
          * @param {XY} p1 - point object with {x,y}
          * @param {XY} p2 - point object with {x,y}
          * @param {Triangle} t Triangle object.
          * @throws {Error} if can't find objects
          */
      Triangle.prototype.markNeighborPointers = function (p1, p2, t) {
        var points = this.points_;
        // Here we are comparing point references, not values
        if (p1 === points[2] && p2 === points[1] || p1 === points[1] && p2 === points[2]) {
          this.neighbors_[0] = t;
        } else if (p1 === points[0] && p2 === points[2] || p1 === points[2] && p2 === points[0]) {
          this.neighbors_[1] = t;
        } else if (p1 === points[0] && p2 === points[1] || p1 === points[1] && p2 === points[0]) {
          this.neighbors_[2] = t;
        } else {
          throw new Error('poly2tri Invalid Triangle.markNeighborPointers() call');
        }
      };

      /**
          * Exhaustive search to update neighbor pointers
          * @private
          * @param {!Triangle} t
          */
      Triangle.prototype.markNeighbor = function (t) {
        var points = this.points_;
        if (t.containsPoints(points[1], points[2])) {
          this.neighbors_[0] = t;
          t.markNeighborPointers(points[1], points[2], this);
        } else if (t.containsPoints(points[0], points[2])) {
          this.neighbors_[1] = t;
          t.markNeighborPointers(points[0], points[2], this);
        } else if (t.containsPoints(points[0], points[1])) {
          this.neighbors_[2] = t;
          t.markNeighborPointers(points[0], points[1], this);
        }
      };


      Triangle.prototype.clearNeighbors = function () {
        this.neighbors_[0] = null;
        this.neighbors_[1] = null;
        this.neighbors_[2] = null;
      };

      Triangle.prototype.clearDelaunayEdges = function () {
        this.delaunay_edge[0] = false;
        this.delaunay_edge[1] = false;
        this.delaunay_edge[2] = false;
      };

      /**
          * Returns the point clockwise to the given point.
          * @private
          * @param {XY} p - point object with {x,y}
          */
      Triangle.prototype.pointCW = function (p) {
        var points = this.points_;
        // Here we are comparing point references, not values
        if (p === points[0]) {
          return points[2];
        } else if (p === points[1]) {
          return points[0];
        } else if (p === points[2]) {
          return points[1];
        } else {
          return null;
        }
      };

      /**
          * Returns the point counter-clockwise to the given point.
          * @private
          * @param {XY} p - point object with {x,y}
          */
      Triangle.prototype.pointCCW = function (p) {
        var points = this.points_;
        // Here we are comparing point references, not values
        if (p === points[0]) {
          return points[1];
        } else if (p === points[1]) {
          return points[2];
        } else if (p === points[2]) {
          return points[0];
        } else {
          return null;
        }
      };

      /**
          * Returns the neighbor clockwise to given point.
          * @private
          * @param {XY} p - point object with {x,y}
          */
      Triangle.prototype.neighborCW = function (p) {
        // Here we are comparing point references, not values
        if (p === this.points_[0]) {
          return this.neighbors_[1];
        } else if (p === this.points_[1]) {
          return this.neighbors_[2];
        } else {
          return this.neighbors_[0];
        }
      };

      /**
          * Returns the neighbor counter-clockwise to given point.
          * @private
          * @param {XY} p - point object with {x,y}
          */
      Triangle.prototype.neighborCCW = function (p) {
        // Here we are comparing point references, not values
        if (p === this.points_[0]) {
          return this.neighbors_[2];
        } else if (p === this.points_[1]) {
          return this.neighbors_[0];
        } else {
          return this.neighbors_[1];
        }
      };

      Triangle.prototype.getConstrainedEdgeCW = function (p) {
        // Here we are comparing point references, not values
        if (p === this.points_[0]) {
          return this.constrained_edge[1];
        } else if (p === this.points_[1]) {
          return this.constrained_edge[2];
        } else {
          return this.constrained_edge[0];
        }
      };

      Triangle.prototype.getConstrainedEdgeCCW = function (p) {
        // Here we are comparing point references, not values
        if (p === this.points_[0]) {
          return this.constrained_edge[2];
        } else if (p === this.points_[1]) {
          return this.constrained_edge[0];
        } else {
          return this.constrained_edge[1];
        }
      };

      // Additional check from Java version (see issue #88)
      Triangle.prototype.getConstrainedEdgeAcross = function (p) {
        // Here we are comparing point references, not values
        if (p === this.points_[0]) {
          return this.constrained_edge[0];
        } else if (p === this.points_[1]) {
          return this.constrained_edge[1];
        } else {
          return this.constrained_edge[2];
        }
      };

      Triangle.prototype.setConstrainedEdgeCW = function (p, ce) {
        // Here we are comparing point references, not values
        if (p === this.points_[0]) {
          this.constrained_edge[1] = ce;
        } else if (p === this.points_[1]) {
          this.constrained_edge[2] = ce;
        } else {
          this.constrained_edge[0] = ce;
        }
      };

      Triangle.prototype.setConstrainedEdgeCCW = function (p, ce) {
        // Here we are comparing point references, not values
        if (p === this.points_[0]) {
          this.constrained_edge[2] = ce;
        } else if (p === this.points_[1]) {
          this.constrained_edge[0] = ce;
        } else {
          this.constrained_edge[1] = ce;
        }
      };

      Triangle.prototype.getDelaunayEdgeCW = function (p) {
        // Here we are comparing point references, not values
        if (p === this.points_[0]) {
          return this.delaunay_edge[1];
        } else if (p === this.points_[1]) {
          return this.delaunay_edge[2];
        } else {
          return this.delaunay_edge[0];
        }
      };

      Triangle.prototype.getDelaunayEdgeCCW = function (p) {
        // Here we are comparing point references, not values
        if (p === this.points_[0]) {
          return this.delaunay_edge[2];
        } else if (p === this.points_[1]) {
          return this.delaunay_edge[0];
        } else {
          return this.delaunay_edge[1];
        }
      };

      Triangle.prototype.setDelaunayEdgeCW = function (p, e) {
        // Here we are comparing point references, not values
        if (p === this.points_[0]) {
          this.delaunay_edge[1] = e;
        } else if (p === this.points_[1]) {
          this.delaunay_edge[2] = e;
        } else {
          this.delaunay_edge[0] = e;
        }
      };

      Triangle.prototype.setDelaunayEdgeCCW = function (p, e) {
        // Here we are comparing point references, not values
        if (p === this.points_[0]) {
          this.delaunay_edge[2] = e;
        } else if (p === this.points_[1]) {
          this.delaunay_edge[0] = e;
        } else {
          this.delaunay_edge[1] = e;
        }
      };

      /**
          * The neighbor across to given point.
          * @private
          * @param {XY} p - point object with {x,y}
          * @returns {Triangle}
          */
      Triangle.prototype.neighborAcross = function (p) {
        // Here we are comparing point references, not values
        if (p === this.points_[0]) {
          return this.neighbors_[0];
        } else if (p === this.points_[1]) {
          return this.neighbors_[1];
        } else {
          return this.neighbors_[2];
        }
      };

      /**
          * @private
          * @param {!Triangle} t Triangle object.
          * @param {XY} p - point object with {x,y}
          */
      Triangle.prototype.oppositePoint = function (t, p) {
        var cw = t.pointCW(p);
        return this.pointCW(cw);
      };

      /**
          * Legalize triangle by rotating clockwise around oPoint
          * @private
          * @param {XY} opoint - point object with {x,y}
          * @param {XY} npoint - point object with {x,y}
          * @throws {Error} if oPoint can not be found
          */
      Triangle.prototype.legalize = function (opoint, npoint) {
        var points = this.points_;
        // Here we are comparing point references, not values
        if (opoint === points[0]) {
          points[1] = points[0];
          points[0] = points[2];
          points[2] = npoint;
        } else if (opoint === points[1]) {
          points[2] = points[1];
          points[1] = points[0];
          points[0] = npoint;
        } else if (opoint === points[2]) {
          points[0] = points[2];
          points[2] = points[1];
          points[1] = npoint;
        } else {
          throw new Error('poly2tri Invalid Triangle.legalize() call');
        }
      };

      /**
          * Returns the index of a point in the triangle. 
          * The point *must* be a reference to one of the triangle's vertices.
          * @private
          * @param {XY} p - point object with {x,y}
          * @returns {number} index 0, 1 or 2
          * @throws {Error} if p can not be found
          */
      Triangle.prototype.index = function (p) {
        var points = this.points_;
        // Here we are comparing point references, not values
        if (p === points[0]) {
          return 0;
        } else if (p === points[1]) {
          return 1;
        } else if (p === points[2]) {
          return 2;
        } else {
          throw new Error('poly2tri Invalid Triangle.index() call');
        }
      };

      /**
          * @private
          * @param {XY} p1 - point object with {x,y}
          * @param {XY} p2 - point object with {x,y}
          * @return {number} index 0, 1 or 2, or -1 if errror
          */
      Triangle.prototype.edgeIndex = function (p1, p2) {
        var points = this.points_;
        // Here we are comparing point references, not values
        if (p1 === points[0]) {
          if (p2 === points[1]) {
            return 2;
          } else if (p2 === points[2]) {
            return 1;
          }
        } else if (p1 === points[1]) {
          if (p2 === points[2]) {
            return 0;
          } else if (p2 === points[0]) {
            return 2;
          }
        } else if (p1 === points[2]) {
          if (p2 === points[0]) {
            return 1;
          } else if (p2 === points[1]) {
            return 0;
          }
        }
        return -1;
      };

      /**
          * Mark an edge of this triangle as constrained.
          * @private
          * @param {number} index - edge index
          */
      Triangle.prototype.markConstrainedEdgeByIndex = function (index) {
        this.constrained_edge[index] = true;
      };
      /**
          * Mark an edge of this triangle as constrained.
          * @private
          * @param {Edge} edge instance
          */
      Triangle.prototype.markConstrainedEdgeByEdge = function (edge) {
        this.markConstrainedEdgeByPoints(edge.p, edge.q);
      };
      /**
          * Mark an edge of this triangle as constrained.
          * This method takes two Point instances defining the edge of the triangle.
          * @private
          * @param {XY} p - point object with {x,y}
          * @param {XY} q - point object with {x,y}
          */
      Triangle.prototype.markConstrainedEdgeByPoints = function (p, q) {
        var points = this.points_;
        // Here we are comparing point references, not values        
        if (q === points[0] && p === points[1] || q === points[1] && p === points[0]) {
          this.constrained_edge[2] = true;
        } else if (q === points[0] && p === points[2] || q === points[2] && p === points[0]) {
          this.constrained_edge[1] = true;
        } else if (q === points[1] && p === points[2] || q === points[2] && p === points[1]) {
          this.constrained_edge[0] = true;
        }
      };


      // ---------------------------------------------------------Exports (public API)

      module.exports = Triangle;

    }, { "./xy": 11 }], 10: [function (require, module, exports) {
      /*
                                                                   * Poly2Tri Copyright (c) 2009-2014, Poly2Tri Contributors
                                                                   * http://code.google.com/p/poly2tri/
                                                                   * 
                                                                   * poly2tri.js (JavaScript port) (c) 2009-2014, Poly2Tri Contributors
                                                                   * https://github.com/r3mi/poly2tri.js
                                                                   * 
                                                                   * All rights reserved.
                                                                   * 
                                                                   * Distributed under the 3-clause BSD License, see LICENSE.txt
                                                                   */

      "use strict";

      /**
                     * Precision to detect repeated or collinear points
                     * @private
                     * @const {number}
                     * @default
                     */
      var EPSILON = 1e-12;
      exports.EPSILON = EPSILON;

      /**
                                  * @private
                                  * @enum {number}
                                  * @readonly
                                  */
      var Orientation = {
        "CW": 1,
        "CCW": -1,
        "COLLINEAR": 0 };

      exports.Orientation = Orientation;


      /**
                                          * Formula to calculate signed area<br>
                                          * Positive if CCW<br>
                                          * Negative if CW<br>
                                          * 0 if collinear<br>
                                          * <pre>
                                          * A[P1,P2,P3]  =  (x1*y2 - y1*x2) + (x2*y3 - y2*x3) + (x3*y1 - y3*x1)
                                          *              =  (x1-x3)*(y2-y3) - (y1-y3)*(x2-x3)
                                          * </pre>
                                          *
                                          * @private
                                          * @param {!XY} pa  point object with {x,y}
                                          * @param {!XY} pb  point object with {x,y}
                                          * @param {!XY} pc  point object with {x,y}
                                          * @return {Orientation}
                                          */
      function orient2d(pa, pb, pc) {
        var detleft = (pa.x - pc.x) * (pb.y - pc.y);
        var detright = (pa.y - pc.y) * (pb.x - pc.x);
        var val = detleft - detright;
        if (val > -EPSILON && val < EPSILON) {
          return Orientation.COLLINEAR;
        } else if (val > 0) {
          return Orientation.CCW;
        } else {
          return Orientation.CW;
        }
      }
      exports.orient2d = orient2d;


      /**
                                    *
                                    * @private
                                    * @param {!XY} pa  point object with {x,y}
                                    * @param {!XY} pb  point object with {x,y}
                                    * @param {!XY} pc  point object with {x,y}
                                    * @param {!XY} pd  point object with {x,y}
                                    * @return {boolean}
                                    */
      function inScanArea(pa, pb, pc, pd) {
        var oadb = (pa.x - pb.x) * (pd.y - pb.y) - (pd.x - pb.x) * (pa.y - pb.y);
        if (oadb >= -EPSILON) {
          return false;
        }

        var oadc = (pa.x - pc.x) * (pd.y - pc.y) - (pd.x - pc.x) * (pa.y - pc.y);
        if (oadc <= EPSILON) {
          return false;
        }
        return true;
      }
      exports.inScanArea = inScanArea;


      /**
                                        * Check if the angle between (pa,pb) and (pa,pc) is obtuse i.e. (angle > π/2 || angle < -π/2)
                                        *
                                        * @private
                                        * @param {!XY} pa  point object with {x,y}
                                        * @param {!XY} pb  point object with {x,y}
                                        * @param {!XY} pc  point object with {x,y}
                                        * @return {boolean} true if angle is obtuse
                                        */
      function isAngleObtuse(pa, pb, pc) {
        var ax = pb.x - pa.x;
        var ay = pb.y - pa.y;
        var bx = pc.x - pa.x;
        var by = pc.y - pa.y;
        return ax * bx + ay * by < 0;
      }
      exports.isAngleObtuse = isAngleObtuse;


    }, {}], 11: [function (require, module, exports) {
      /*
                                                       * Poly2Tri Copyright (c) 2009-2014, Poly2Tri Contributors
                                                       * http://code.google.com/p/poly2tri/
                                                       * 
                                                       * poly2tri.js (JavaScript port) (c) 2009-2014, Poly2Tri Contributors
                                                       * https://github.com/r3mi/poly2tri.js
                                                       * 
                                                       * All rights reserved.
                                                       * 
                                                       * Distributed under the 3-clause BSD License, see LICENSE.txt
                                                       */

      "use strict";

      /**
                     * The following functions operate on "Point" or any "Point like" object with {x,y},
                     * as defined by the {@link XY} type
                     * ([duck typing]{@link http://en.wikipedia.org/wiki/Duck_typing}).
                     * @module
                     * @private
                     */

      /**
                         * poly2tri.js supports using custom point class instead of {@linkcode Point}.
                         * Any "Point like" object with <code>{x, y}</code> attributes is supported
                         * to initialize the SweepContext polylines and points
                         * ([duck typing]{@link http://en.wikipedia.org/wiki/Duck_typing}).
                         *
                         * poly2tri.js might add extra fields to the point objects when computing the
                         * triangulation : they are prefixed with <code>_p2t_</code> to avoid collisions
                         * with fields in the custom class.
                         *
                         * @example
                         *      var contour = [{x:100, y:100}, {x:100, y:300}, {x:300, y:300}, {x:300, y:100}];
                         *      var swctx = new poly2tri.SweepContext(contour);
                         *
                         * @typedef {Object} XY
                         * @property {number} x - x coordinate
                         * @property {number} y - y coordinate
                         */


      /**
                             * Point pretty printing : prints x and y coordinates.
                             * @example
                             *      xy.toStringBase({x:5, y:42})
                             *      // → "(5;42)"
                             * @protected
                             * @param {!XY} p - point object with {x,y}
                             * @returns {string} <code>"(x;y)"</code>
                             */
      function toStringBase(p) {
        return "(" + p.x + ";" + p.y + ")";
      }

      /**
         * Point pretty printing. Delegates to the point's custom "toString()" method if exists,
         * else simply prints x and y coordinates.
         * @example
         *      xy.toString({x:5, y:42})
         *      // → "(5;42)"
         * @example
         *      xy.toString({x:5,y:42,toString:function() {return this.x+":"+this.y;}})
         *      // → "5:42"
         * @param {!XY} p - point object with {x,y}
         * @returns {string} <code>"(x;y)"</code>
         */
      function toString(p) {
        // Try a custom toString first, and fallback to own implementation if none
        var s = p.toString();
        return s === '[object Object]' ? toStringBase(p) : s;
      }


      /**
         * Compare two points component-wise. Ordered by y axis first, then x axis.
         * @param {!XY} a - point object with {x,y}
         * @param {!XY} b - point object with {x,y}
         * @return {number} <code>&lt; 0</code> if <code>a &lt; b</code>,
         *         <code>&gt; 0</code> if <code>a &gt; b</code>, 
         *         <code>0</code> otherwise.
         */
      function compare(a, b) {
        if (a.y === b.y) {
          return a.x - b.x;
        } else {
          return a.y - b.y;
        }
      }

      /**
         * Test two Point objects for equality.
         * @param {!XY} a - point object with {x,y}
         * @param {!XY} b - point object with {x,y}
         * @return {boolean} <code>True</code> if <code>a == b</code>, <code>false</code> otherwise.
         */
      function equals(a, b) {
        return a.x === b.x && a.y === b.y;
      }


      module.exports = {
        toString: toString,
        toStringBase: toStringBase,
        compare: compare,
        equals: equals };


    }, {}] }, {}, [6])(6);
});
/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../node_modules/webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js")))

/***/ }),

/***/ "./extensions/CompGeom/quad-tree.js":
/*!******************************************!*\
  !*** ./extensions/CompGeom/quad-tree.js ***!
  \******************************************/
/*! exports provided: QuadTree */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "QuadTree", function() { return QuadTree; });
/* harmony import */ var _x_line_box__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./x-line-box */ "./extensions/CompGeom/x-line-box.js");
/* harmony import */ var _x_box_box__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./x-box-box */ "./extensions/CompGeom/x-box-box.js");
function _classCallCheck(instance, Constructor) {if (!(instance instanceof Constructor)) {throw new TypeError("Cannot call a class as a function");}}function _defineProperties(target, props) {for (var i = 0; i < props.length; i++) {var descriptor = props[i];descriptor.enumerable = descriptor.enumerable || false;descriptor.configurable = true;if ("value" in descriptor) descriptor.writable = true;Object.defineProperty(target, descriptor.key, descriptor);}}function _createClass(Constructor, protoProps, staticProps) {if (protoProps) _defineProperties(Constructor.prototype, protoProps);if (staticProps) _defineProperties(Constructor, staticProps);return Constructor;}



//Spatial index data structure for fast lookup of line segments

var ITEMS_PER_NODE = 16;
var EPS = 1e-20;


var QuadTree = /*#__PURE__*/function () {

  function QuadTree(minx, miny, maxx, maxy, extraDistance) {_classCallCheck(this, QuadTree);

    this.items = [];
    this.children = null;
    this.itemCount = 0;

    this.extraDistance = extraDistance;

    this.minx = minx;
    this.miny = miny;
    this.maxx = maxx;
    this.maxy = maxy;
  }_createClass(QuadTree, [{ key: "addItem", value: function addItem(


    e) {

      //TODO: must check if item fits inside our total bbox
      //before adding. In such case we may have to expand the
      //tree somehow

      this.itemCount++;

      if (this.children) {
        var overlapCount = 0;
        var whichChild = null;

        for (var i = 0; i < 4; i++) {
          if (this.children[i].intersectsItem(e)) {
            whichChild = this.children[i];
            overlapCount++;
          }
        }

        if (overlapCount === 0) {

        } else if (overlapCount === 1) {
          whichChild.addItem(e);
        } else {
          this.items.push(e);
        }

      } else {
        this.items.push(e);

        if (this.items.length > ITEMS_PER_NODE)
        this.subdivide();
      }

      return this.itemCount;
    } }, { key: "deleteItem", value: function deleteItem(

    e) {

      if (!this.intersectsItem(e))
      return 0;

      if (this.items) {
        var idx = this.items.indexOf(e);
        if (idx >= 0) {
          this.items.splice(idx, 1);
          this.itemCount--;
          return 1;
        }
      }

      if (this.children) {
        var deleteCount = 0;
        var remainingItemsCount = 0;
        for (var i = 0; i < 4; i++) {
          deleteCount += this.children[i].deleteItem(e);
          remainingItemsCount += this.children[i].itemCount;
        }

        if (remainingItemsCount < ITEMS_PER_NODE) {
          //TODO: un-split the node here
        }

        if (deleteCount === 1) {
          this.itemCount--;
          return 1;
        } else {
          console.warn("Did not find item to delete. Something is wrong.", deleteCount);
          return 0;
        }
      }

      return 0;
    } }, { key: "intersectsBox", value: function intersectsBox(

    minx, miny, maxx, maxy) {

      var d = this.extraDistance;

      return Object(_x_box_box__WEBPACK_IMPORTED_MODULE_1__["xBoxBox"])(minx, miny, maxx, maxy,
      this.minx - d, this.miny - d, this.maxx + d, this.maxy + d);
    } }, { key: "intersectsItem", value: function intersectsItem(

    e) {
      //Meh... polymorphism seems like overkill if all we want to support here is
      //edges and vertices...
      if (e.v1) {
        //Edge
        return Object(_x_line_box__WEBPACK_IMPORTED_MODULE_0__["xLineBox"])(e.v1.x, e.v1.y, e.v2.x, e.v2.y,
        this.minx - this.extraDistance, this.miny - this.extraDistance,
        this.maxx + this.extraDistance, this.maxy + this.extraDistance);
      } else {
        //Vertex
        return this.intersectsBox(e.x, e.y, e.x, e.y);
      }
    } }, { key: "findSplitPoint", value: function findSplitPoint()

    {
      //determine split location -- we split along the
      //midpoint of actual data inside the node
      var xs = [];
      var ys = [];

      //Meh... polymorphism seems like overkill if all we want to support here is
      //edges and vertices...
      if (this.items[0].v1) {
        for (var i = 0; i < this.items.length; i++) {
          xs.push(this.items[i].v1.x);
          ys.push(this.items[i].v1.y);
        }
      } else {
        for (var _i = 0; _i < this.items.length; _i++) {
          xs.push(this.items[_i].x);
          ys.push(this.items[_i].y);
        }
      }

      xs.sort(function (a, b) {return a - b;});
      ys.sort(function (a, b) {return a - b;});

      //Split slightly to the left of the median min point for all edge items
      var midx = xs[0 | (xs.length + 1) / 2] - this.extraDistance - EPS;
      var midy = ys[0 | (ys.length + 1) / 2] - this.extraDistance - EPS;

      if (midx <= this.minx || midx >= this.maxx || midy <= this.miny || midy >= this.maxy) {
        console.warn("Failed to split quad tree node. Something is wrong with the split choice.");
        return null;
      }

      return { midx: midx, midy: midy };
    } }, { key: "subdivide", value: function subdivide()


    {

      if (this.children) {
        console.error("Attempt to subdivide already split node");
        return;
      }

      if (!this.items.length) {
        console.error("Attempt to subdivide empty node");
        return;
      }

      var minx = this.minx;
      var miny = this.miny;
      var maxx = this.maxx;
      var maxy = this.maxy;

      //determine split location -- we split along the
      //midpoint of actual data inside the node
      var split = this.findSplitPoint();

      if (!split) {
        console.warn("Failed to split node");
        return;
      }var

      midx = split.midx,midy = split.midy;

      this.children = new Array(4);
      this.children[0] = new QuadTree(minx, miny, midx, midy, this.extraDistance);
      this.children[1] = new QuadTree(midx, miny, maxx, midy, this.extraDistance);
      this.children[2] = new QuadTree(midx, midy, maxx, maxy, this.extraDistance);
      this.children[3] = new QuadTree(minx, midy, midx, maxy, this.extraDistance);

      var keepItems = [];

      for (var i = 0, iEnd = this.items.length; i < iEnd; i++) {

        var overlapCount = 0;
        var whichChild = null;

        for (var j = 0; j < 4; j++) {
          if (this.children[j].intersectsItem(this.items[i])) {
            whichChild = this.children[j];
            overlapCount++;
          }
        }

        if (overlapCount === 0) {
          console.error("Expected at least one overlap");
        } else if (overlapCount === 1) {
          whichChild.addItem(this.items[i]);
        } else {
          keepItems.push(this.items[i]);
        }
      }

      this.items = keepItems;
    } }, { key: "enumNearItems", value: function enumNearItems(

    e, cb) {

      if (!this.intersectsItem(e))
      return;

      if (this.items) {
        for (var i = 0; i < this.items.length; i++) {
          cb(this.items[i]);
        }
      }

      if (this.children) {
        for (var _i2 = 0; _i2 < 4; _i2++) {
          this.children[_i2].enumNearItems(e, cb);
        }
      }

    } }, { key: "enumInBox", value: function enumInBox(

    minx, miny, maxx, maxy, cb) {

      if (!this.intersectsBox(minx, miny, maxx, maxy))
      return;

      if (this.items) {
        for (var i = 0; i < this.items.length; i++) {
          var e = this.items[i];

          if (e.v1) {
            if (Object(_x_line_box__WEBPACK_IMPORTED_MODULE_0__["xLineBox"])(e.v1.x, e.v1.y, e.v2.x, e.v2.y, minx, miny, maxx, maxy))
            cb(e);
          } else {
            if (Object(_x_box_box__WEBPACK_IMPORTED_MODULE_1__["xBoxBox"])(e.x, e.y, e.x, e.y, minx, miny, maxx, maxy))
            cb(e);
          }
        }
      }

      if (this.children) {
        for (var _i3 = 0; _i3 < 4; _i3++) {
          this.children[_i3].enumInBox(minx, miny, maxx, maxy, cb);
        }
      }

    } }, { key: "pointInPolygonRec", value: function pointInPolygonRec(


    e, x, y) {

      // get the last point in the polygon
      var vtx0X = e.v1.x;
      var vtx0Y = e.v1.y;

      // get test bit for above/below X axis
      var yflag0 = vtx0Y >= y;

      var vtx1X = e.v2.x;
      var vtx1Y = e.v2.y;

      var yflag1 = vtx1Y >= y;

      // Check if endpoints straddle (are on opposite sides) of X axis
      // (i.e. the Y's differ); if so, +X ray could intersect this edge.
      // The old test also checked whether the endpoints are both to the
      // right or to the left of the test point.  However, given the faster
      // intersection point computation used below, this test was found to
      // be a break-even proposition for most polygons and a loser for
      // triangles (where 50% or more of the edges which survive this test
      // will cross quadrants and so have to have the X intersection computed
      // anyway).  I credit Joseph Samosky with inspiring me to try dropping
      // the "both left or both right" part of my code.
      if (yflag0 != yflag1)
      {
        // Check intersection of pgon segment with +X ray.
        // Note if >= point's X; if so, the ray hits it.
        // The division operation is avoided for the ">=" test by checking
        // the sign of the first vertex wrto the test point; idea inspired
        // by Joseph Samosky's and Mark Haigh-Hutchinson's different
        // polygon inclusion tests.
        if ((vtx1Y - y) * (vtx0X - vtx1X) >=
        (vtx1X - x) * (vtx0Y - vtx1Y) == yflag1)
        {
          this.pipResult = !this.pipResult;
        }
      }

    } }, { key: "pointInPolygon", value: function pointInPolygon(

    x, y) {var _this = this;

      this.pipResult = false;

      this.enumInBox(-Infinity, y, Infinity, y, function (item) {

        _this.pointInPolygonRec(item, x, y);

      });

      return this.pipResult;

    } }]);return QuadTree;}();

/***/ }),

/***/ "./extensions/CompGeom/x-box-box.js":
/*!******************************************!*\
  !*** ./extensions/CompGeom/x-box-box.js ***!
  \******************************************/
/*! exports provided: xBoxBox */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "xBoxBox", function() { return xBoxBox; });

function xBoxBox(minx1, miny1, maxx1, maxy1,
minx2, miny2, maxx2, maxy2) {

  return minx1 <= maxx2 &&
  miny1 <= maxy2 &&
  maxx1 >= minx2 &&
  maxy1 >= miny2;

}

/***/ }),

/***/ "./extensions/CompGeom/x-box-plane.js":
/*!********************************************!*\
  !*** ./extensions/CompGeom/x-box-plane.js ***!
  \********************************************/
/*! exports provided: xBoxPlane */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "xBoxPlane", function() { return xBoxPlane; });

var point = new THREE.Vector3();

function xBoxPlane(plane, box) {

  point.set(box.min.x, box.min.y, box.min.z); // 000
  var d = plane.distanceToPoint(point);
  var s = Math.sign(d);

  point.set(box.min.x, box.min.y, box.max.z); // 001
  var d2 = plane.distanceToPoint(point);
  if (Math.sign(d2) !== s)
  return true;

  point.set(box.min.x, box.max.y, box.min.z); // 010
  d2 = plane.distanceToPoint(point);
  if (Math.sign(d2) !== s)
  return true;

  point.set(box.min.x, box.max.y, box.max.z); // 011
  d2 = plane.distanceToPoint(point);
  if (Math.sign(d2) !== s)
  return true;

  point.set(box.max.x, box.min.y, box.min.z); // 100
  d2 = plane.distanceToPoint(point);
  if (Math.sign(d2) !== s)
  return true;

  point.set(box.max.x, box.min.y, box.max.z); // 101
  d2 = plane.distanceToPoint(point);
  if (Math.sign(d2) !== s)
  return true;

  point.set(box.max.x, box.max.y, box.min.z); // 110
  d2 = plane.distanceToPoint(point);
  if (Math.sign(d2) !== s)
  return true;

  point.set(box.max.x, box.max.y, box.max.z); // 111
  d2 = plane.distanceToPoint(point);
  if (Math.sign(d2) !== s)
  return true;

  return false;
}

/***/ }),

/***/ "./extensions/CompGeom/x-line-box.js":
/*!*******************************************!*\
  !*** ./extensions/CompGeom/x-line-box.js ***!
  \*******************************************/
/*! exports provided: xLineBox */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "xLineBox", function() { return xLineBox; });

var INSIDE = 0; // 0000
var LEFT = 1; // 0001
var RIGHT = 2; // 0010
var BOTTOM = 4; // 0100
var TOP = 8; // 1000

// Compute the bit code for a point (x, y) using the clip rectangle
// bounded diagonally by (xmin, ymin), and (xmax, ymax)

function ComputeOutCode(x, y, xmin, ymin, xmax, ymax)
{
  var code;

  code = INSIDE; // initialised as being inside of [[clip window]]

  if (x < xmin) // to the left of clip window
    code |= LEFT;else
  if (x > xmax) // to the right of clip window
    code |= RIGHT;
  if (y < ymin) // below the clip window
    code |= BOTTOM;else
  if (y > ymax) // above the clip window
    code |= TOP;

  return code;
}

// Cohen–Sutherland clipping algorithm clips a line from
// P0 = (x0, y0) to P1 = (x1, y1) against a rectangle with
// diagonal from (xmin, ymin) to (xmax, ymax).
function xLineBox(x0, y0, x1, y1, xmin, ymin, xmax, ymax)
{
  // compute outcodes for P0, P1, and whatever point lies outside the clip rectangle
  var outcode0 = ComputeOutCode(x0, y0, xmin, ymin, xmax, ymax);
  var outcode1 = ComputeOutCode(x1, y1, xmin, ymin, xmax, ymax);
  var accept = false;

  while (true) {
    if (!(outcode0 | outcode1)) {
      // bitwise OR is 0: both points inside window; trivially accept and exit loop
      accept = true;
      break;
    } else if (outcode0 & outcode1) {
      // bitwise AND is not 0: both points share an outside zone (LEFT, RIGHT, TOP,
      // or BOTTOM), so both must be outside window; exit loop (accept is false)
      break;
    } else {
      // failed both tests, so calculate the line segment to clip
      // from an outside point to an intersection with clip edge
      var x = void 0,y = void 0;

      // At least one endpoint is outside the clip rectangle; pick it.
      var outcodeOut = outcode0 ? outcode0 : outcode1;

      // Now find the intersection point;
      // use formulas:
      //   slope = (y1 - y0) / (x1 - x0)
      //   x = x0 + (1 / slope) * (ym - y0), where ym is ymin or ymax
      //   y = y0 + slope * (xm - x0), where xm is xmin or xmax
      // No need to worry about divide-by-zero because, in each case, the
      // outcode bit being tested guarantees the denominator is non-zero
      if (outcodeOut & TOP) {// point is above the clip window
        x = x0 + (x1 - x0) * (ymax - y0) / (y1 - y0);
        y = ymax;
      } else if (outcodeOut & BOTTOM) {// point is below the clip window
        x = x0 + (x1 - x0) * (ymin - y0) / (y1 - y0);
        y = ymin;
      } else if (outcodeOut & RIGHT) {// point is to the right of clip window
        y = y0 + (y1 - y0) * (xmax - x0) / (x1 - x0);
        x = xmax;
      } else if (outcodeOut & LEFT) {// point is to the left of clip window
        y = y0 + (y1 - y0) * (xmin - x0) / (x1 - x0);
        x = xmin;
      }

      // Now we move outside point to intersection point to clip
      // and get ready for next pass.
      if (outcodeOut === outcode0) {
        x0 = x;
        y0 = y;
        outcode0 = ComputeOutCode(x0, y0, xmin, ymin, xmax, ymax);
      } else {
        x1 = x;
        y1 = y;
        outcode1 = ComputeOutCode(x1, y1, xmin, ymin, xmax, ymax);
      }
    }
  }

  return accept;
}

/***/ }),

/***/ "./extensions/CompGeom/x-mesh-plane.js":
/*!*********************************************!*\
  !*** ./extensions/CompGeom/x-mesh-plane.js ***!
  \*********************************************/
/*! exports provided: xMeshPlane, makePlaneBasis, convertToPlaneCoords */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "xMeshPlane", function() { return xMeshPlane; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "makePlaneBasis", function() { return makePlaneBasis; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "convertToPlaneCoords", function() { return convertToPlaneCoords; });
/* harmony import */ var _fuzzy_math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./fuzzy-math */ "./extensions/CompGeom/fuzzy-math.js");
/* harmony import */ var _x_triangle_plane__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./x-triangle-plane */ "./extensions/CompGeom/x-triangle-plane.js");



//const THREE = THREE;
var avp = Autodesk.Viewing.Private;
var VertexEnumerator = avp.VertexEnumerator;





var mi = new THREE.Matrix4();
var pi = new THREE.Plane();

function xMeshPlane(plane, mesh, intersects) {

  var geometry = mesh.geometry;

  if (!geometry)
  return;

  var baseIndex = intersects.length;

  var matrixWorld = mesh.matrixWorld;
  mi.getInverse(matrixWorld);
  pi.copy(plane).applyMatrix4(mi);

  VertexEnumerator.enumMeshTriangles(geometry, function (vA, vB, vC, a, b, c) {

    Object(_x_triangle_plane__WEBPACK_IMPORTED_MODULE_1__["xTrianglePlane"])(pi, vA, vB, vC, a, b, c, intersects, mesh.fragId);

  });

  //Put the points into world space. It should actually be possible to do
  //the entire math in object space -- but we have to check if all fragments
  //that belong to the same dbId have the same world transform.
  for (var i = baseIndex, iEnd = intersects.length; i < iEnd; i++) {
    intersects[i].v1.applyMatrix4(matrixWorld);
    intersects[i].v2.applyMatrix4(matrixWorld);
  }

}


function makeRotationAxis(axis, cosa, m) {

  // Based on http://www.gamedev.net/reference/articles/article1199.asp

  var c = cosa;
  var s = Math.sqrt(1.0 - c * c);
  var t = 1 - c;
  var x = axis.x,y = axis.y,z = axis.z;
  var tx = t * x,ty = t * y;

  m.set(

  tx * x + c, tx * y - s * z, tx * z + s * y, 0,
  tx * y + s * z, ty * y + c, ty * z - s * x, 0,
  tx * z - s * y, ty * z + s * x, t * z * z + c, 0,
  0, 0, 0, 1);



}


function makePlaneBasis(plane) {

  //var origin = plane.coplanarPoint();

  var sceneUp = new THREE.Vector3(0, 0, 1);
  var cross = plane.normal.clone().cross(sceneUp);
  cross = cross.normalize();
  var dot = sceneUp.dot(plane.normal);

  //We are ignoring the translation here, since
  //we will drop the Z coord for the 2D processing steps anyway.
  var planeBasis = new THREE.Matrix4();

  if (!(Object(_fuzzy_math__WEBPACK_IMPORTED_MODULE_0__["isZero"])(cross.x) && Object(_fuzzy_math__WEBPACK_IMPORTED_MODULE_0__["isZero"])(cross.y) && Object(_fuzzy_math__WEBPACK_IMPORTED_MODULE_0__["isZero"])(cross.z))) {
    makeRotationAxis(cross, dot, planeBasis);
    planeBasis.elements[14] = plane.constant;
  } else {
    planeBasis.elements[14] = dot * plane.constant;
  }

  return planeBasis;
}


function convertToPlaneCoords(planeBasis, edges3d, bbox) {

  for (var i = 0; i < edges3d.length; i++) {
    var e = edges3d[i];

    e.v1.applyMatrix4(planeBasis);
    e.v2.applyMatrix4(planeBasis);

    bbox.expandByPoint(e.v1);
    bbox.expandByPoint(e.v2);
  }
}

/***/ }),

/***/ "./extensions/CompGeom/x-plane-segment.js":
/*!************************************************!*\
  !*** ./extensions/CompGeom/x-plane-segment.js ***!
  \************************************************/
/*! exports provided: xPlaneSegment */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "xPlaneSegment", function() { return xPlaneSegment; });
/* harmony import */ var _fuzzy_math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./fuzzy-math */ "./extensions/CompGeom/fuzzy-math.js");



var v1 = new THREE.Vector3();

function xPlaneSegment(plane, pt0, pt1, res1, res2) {

  var direction = v1.subVectors(pt1, pt0);

  var denominator = plane.normal.dot(direction);

  if (Object(_fuzzy_math__WEBPACK_IMPORTED_MODULE_0__["isZero"])(denominator)) {

    res1.copy(pt0);
    res2.copy(pt1);

    // line is coplanar
    return 2;
  }

  denominator = 1.0 / denominator;

  var t = -(pt0.dot(plane.normal) * denominator + plane.constant * denominator);

  if (t < -_fuzzy_math__WEBPACK_IMPORTED_MODULE_0__["TOL"] || t > 1 + _fuzzy_math__WEBPACK_IMPORTED_MODULE_0__["TOL"]) {

    return 0;

  }

  var pt = direction.multiplyScalar(t).add(pt0);

  res1.copy(pt);

  return 1;
}

/***/ }),

/***/ "./extensions/CompGeom/x-triangle-plane.js":
/*!*************************************************!*\
  !*** ./extensions/CompGeom/x-triangle-plane.js ***!
  \*************************************************/
/*! exports provided: xTrianglePlane */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "xTrianglePlane", function() { return xTrianglePlane; });
/* harmony import */ var _fuzzy_math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./fuzzy-math */ "./extensions/CompGeom/fuzzy-math.js");
/* harmony import */ var _x_plane_segment__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./x-plane-segment */ "./extensions/CompGeom/x-plane-segment.js");




var logger = console;


function Edge(pt1, pt2, id1From, id1To, id2From, id2To, meshId) {

  this.v1 = pt1.clone();
  this.v2 = pt2.clone();

}



var res1 = new THREE.Vector3();
var res2 = new THREE.Vector3();

// res is array containing result segments.
// returns number of intersection point on the plane (0, 1, or 2) with the values of the points stored in the res array
function xTrianglePlane(plane, pt0, pt1, pt2, i0, i1, i2, res, meshId) {

  var d0 = plane.distanceToPoint(pt0);
  var d1 = plane.distanceToPoint(pt1);
  var d2 = plane.distanceToPoint(pt2);

  // Check if all points are to one side of the plane
  if (d0 < -_fuzzy_math__WEBPACK_IMPORTED_MODULE_0__["TOL"] && d1 < -_fuzzy_math__WEBPACK_IMPORTED_MODULE_0__["TOL"] && d2 < -_fuzzy_math__WEBPACK_IMPORTED_MODULE_0__["TOL"]) {
    return null;
  }
  if (d0 > _fuzzy_math__WEBPACK_IMPORTED_MODULE_0__["TOL"] && d1 > _fuzzy_math__WEBPACK_IMPORTED_MODULE_0__["TOL"] && d2 > _fuzzy_math__WEBPACK_IMPORTED_MODULE_0__["TOL"]) {
    return null;
  }

  var s0 = Math.sign(d0);
  var s1 = Math.sign(d1);
  var s2 = Math.sign(d2);

  // Skip coplanar triangles (leave it to the neighbouring triangles to contribute their edges)
  if (s0 === 0 && s1 === 0 && s2 === 0) {
    return null;
  }

  var tmp1, tmp2;
  var i1From, i1To, i2From, i2To;

  //There is intersection, compute it
  if (s0 !== s1) {
    var numInts = Object(_x_plane_segment__WEBPACK_IMPORTED_MODULE_1__["xPlaneSegment"])(plane, pt0, pt1, res1, res2);
    if (numInts === 2) {
      res.push(new Edge(pt0, pt1, i0, i0, i1, i1, meshId));
      return;
    } else if (numInts === 1) {
      i1From = i0;
      i1To = i1;
      tmp1 = res1.clone();
    } else {
      logger.warn("Unexpected zero intersections where at least one was expected");
    }
  }

  if (s1 !== s2) {
    var _numInts = Object(_x_plane_segment__WEBPACK_IMPORTED_MODULE_1__["xPlaneSegment"])(plane, pt1, pt2, res1, res2);
    if (_numInts === 2) {
      res.push(new Edge(pt1, pt2, i1, i1, i2, i2, meshId));
      return;
    } else if (_numInts === 1) {
      if (tmp1) {
        // Avoid the singular scenario where the signs are 0, -1 and +1
        if (res1.distanceTo(tmp1) > _fuzzy_math__WEBPACK_IMPORTED_MODULE_0__["TOL"]) {
          i2From = i1;
          i2To = i2;
          tmp2 = res1.clone();
        }
      } else
      {
        i1From = i1;
        i1To = i2;
        tmp1 = res1.clone();
      }
    } else {
      logger.warn("Unexpected zero intersections where at least one was expected");
    }
  }

  if (s2 !== s0) {
    var _numInts2 = Object(_x_plane_segment__WEBPACK_IMPORTED_MODULE_1__["xPlaneSegment"])(plane, pt2, pt0, res1, res2);
    if (_numInts2 === 2) {
      res.push(new Edge(pt2, pt0, i2, i2, i0, i0, meshId));
      return;
    } else if (_numInts2 === 1) {
      if (tmp1) {
        // Avoid the singular scenario where the signs are 0, -1 and +1
        if (res1.distanceTo(tmp1) > _fuzzy_math__WEBPACK_IMPORTED_MODULE_0__["TOL"]) {
          i2From = i2;
          i2To = i0;
          tmp2 = res1.clone();
        }
      } else {
        logger.warn("Unexpected single intersection point");
      }
    } else {
      logger.warn("Unexpected zero intersections where at least one was expected");
    }
  }


  if (tmp1 && tmp2) {
    res.push(new Edge(tmp1, tmp2, i1From, i1To, i2From, i2To, meshId));
  } else {
    //logger.warn("Unexpected one intersection where two were expected");
  }

}

/***/ }),

/***/ "./extensions/Section/Section.css":
/*!****************************************!*\
  !*** ./extensions/Section/Section.css ***!
  \****************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {


var content = __webpack_require__(/*! !../../node_modules/css-loader!./Section.css */ "./node_modules/css-loader/index.js!./extensions/Section/Section.css");

if(typeof content === 'string') content = [[module.i, content, '']];

var transform;
var insertInto;



var options = {"hmr":true}

options.transform = transform
options.insertInto = undefined;

var update = __webpack_require__(/*! ../../node_modules/style-loader/lib/addStyles.js */ "./node_modules/style-loader/lib/addStyles.js")(content, options);

if(content.locals) module.exports = content.locals;

if(false) {}

/***/ }),

/***/ "./extensions/Section/Section.js":
/*!***************************************!*\
  !*** ./extensions/Section/Section.js ***!
  \***************************************/
/*! exports provided: SectionExtension */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SectionExtension", function() { return SectionExtension; });
/* harmony import */ var _SectionTool__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./SectionTool */ "./extensions/Section/SectionTool.js");
/* harmony import */ var _Section_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Section.css */ "./extensions/Section/Section.css");
/* harmony import */ var _Section_css__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_Section_css__WEBPACK_IMPORTED_MODULE_1__);



/**
                                  * The SectionExtension provides ways to cut the geometry using planes or a cube.
                                  * The extension adds a toolbar button to access the feature. 
                                  * 
                                  * The extension id is: `Autodesk.Section`
                                  * 
                                  * @example
                                  *   viewer.loadExtension('Autodesk.Section')
                                  * 
                                  * @memberof Autodesk.Viewing.Extensions
                                  * @alias Autodesk.Viewing.Extensions.SectionExtension
                                  * @see {@link Autodesk.Viewing.Extension} for common inherited methods.
                                  * @constructor
                                  */
var SectionExtension = function SectionExtension(viewer, options) {
  Autodesk.Viewing.Extension.call(this, viewer, options);
  this.viewer = viewer;
  this.name = 'section';
  this.modes = ['x', 'y', 'z', 'box'];

  this.onResetBinded = this.onReset.bind(this);
  this.onViewerSetView = this.onViewerSetView.bind(this);
};

SectionExtension.prototype = Object.create(Autodesk.Viewing.Extension.prototype);
SectionExtension.prototype.constructor = SectionExtension;

var proto = SectionExtension.prototype;

/**
                                         * Registers the SectionTool, hotkeys and event handlers.
                                         *
                                         * @returns {boolean}
                                         */
proto.load = function () {
  var that = this;
  var viewer = this.viewer;

  this.tool = new _SectionTool__WEBPACK_IMPORTED_MODULE_0__["SectionTool"](viewer, {
    tintColor: { r: 1, g: 1, b: 0 },
    tintIntensity: 0.2 });

  viewer.toolController.registerTool(this.tool);
  this.sectionStyle = null;
  this.supportedStyles = ["X", "Y", "Z", "BOX"];

  if (viewer.getToolbar) {
    var toolbar = viewer.getToolbar(true);
    if (toolbar) {
      this.onToolbarCreated();
    } else {
      this.onToolbarCreatedBinded = this.onToolbarCreated.bind(this);
      viewer.addEventListener(Autodesk.Viewing.TOOLBAR_CREATED_EVENT, this.onToolbarCreatedBinded);
    }
  }

  viewer.addEventListener(Autodesk.Viewing.RESET_EVENT, this.onResetBinded);
  viewer.addEventListener(Autodesk.Viewing.SET_VIEW_EVENT, this.onViewerSetView);

  this.HOTKEYS_ID = "Autodesk.Section.Hotkeys";
  var hotkeys = [{
    keycodes: [
    Autodesk.Viewing.KeyCode.ESCAPE],

    onRelease: function onRelease() {
      return that.enableSectionTool(false);
    } }];

  viewer.getHotkeyManager().pushHotkeys(this.HOTKEYS_ID, hotkeys);

  return true;
};

/**
    * Unregisters the SectionTool, hotkeys and event handlers.
    *
    * @returns {boolean}
    */
proto.unload = function () {
  var viewer = this.viewer;

  // remove hotkey
  viewer.getHotkeyManager().popHotkeys(this.HOTKEYS_ID);

  this.destroyUI();

  viewer.removeEventListener(Autodesk.Viewing.RESET_EVENT, this.onResetBinded);
  viewer.removeEventListener(Autodesk.Viewing.SET_VIEW_EVENT, this.onViewerSetView);

  if (this.onToolbarCreatedBinded) {
    viewer.removeEventListener(Autodesk.Viewing.TOOLBAR_CREATED_EVENT, this.onToolbarCreatedBinded);
    this.onToolbarCreatedBinded = null;
  }

  viewer.toolController.deregisterTool(this.tool);
  this.tool = null;

  return true;
};

/**
    * Toggles activeness of section planes.
    * @returns {boolean} Whether the section plane is active or not.
    * @alias Autodesk.Viewing.Extensions.SectionExtension#toggle
    */
proto.toggle = function () {
  if (this.isActive()) {
    this.enableSectionTool(false);
  } else {
    var style = this.sectionStyle || "X";
    this.setSectionStyle(style, true);
  }
  return this.isActive(); // Need to check for isActive() again.
};

/**
    * Returns the current type of plane that will cut-though the geometry.
    *
    * @returns {null|String} Either "X" or "Y" or "Z" or "BOX" or null.
    * @alias Autodesk.Viewing.Extensions.SectionExtension#getSectionStyle
    */
proto.getSectionStyle = function () {
  return this.sectionStyle;
};

/**
    * Sets the Section plane style.
    *
    * @param {String} style - Accepted values are 'X', 'Y', 'Z' and 'BOX' (in Caps)
    * @param {Boolean} [preserveSection] - Whether sending the current style value resets the cut planes.
    * @alias Autodesk.Viewing.Extensions.SectionExtension#setSectionStyle
    */
proto.setSectionStyle = function (style, preserveSection) {

  if (this.supportedStyles.indexOf(style) === -1) {
    return false;
  }

  var bActive = this.isActive();
  var bNewStyle = this.sectionStyle !== style || !preserveSection;
  this.sectionStyle = style;

  if (bActive && bNewStyle) {
    this.tool.setSection(style);
  } else
  if (!bActive) {
    this.enableSectionTool(true);
    if (bNewStyle) {
      this.tool.setSection(style);
    } else {
      this.tool.attachControl(true);
    }
  }
  return true;
};

/**
    * Use to set the section from an externally defined plane. For showing with line pattern
    * Tool itself will be disabled when setting the plane
    *
    * @param {THREE.Vector4} cutplane - send null to clear the section
    */
proto.setSectionFromPlane = function (cutplane) {
  if (cutplane) {
    this.deactivate();
    this.tool.setSectionFromPlane(cutplane);
    this.tool.attachControl(false);
  } else {
    this.tool.clearSection();
    var prevLock = this.viewer.toolController.setIsLocked(false);
    this.enableSectionTool(false);
    this.viewer.toolController.setIsLocked(prevLock);
  }
};

/**
    *
    * @param enable
    * @returns {boolean}
    * @private
    */
proto.enableSectionTool = function (enable) {
  var toolController = this.viewer.toolController,
  isActive = this.tool.isActive();

  if (enable && !isActive) {
    toolController.activateTool("section");
    if (this.sectionToolButton) {
      this.sectionToolButton.setState(Autodesk.Viewing.UI.Button.State.ACTIVE);
    }
    return true;

  } else if (!enable && isActive) {
    toolController.deactivateTool("section");
    if (this.sectionToolButton) {
      this.sectionToolButton.setState(Autodesk.Viewing.UI.Button.State.INACTIVE);
    }
    return true;
  }
  return false;
};

/**
    * Returns an object that reperesents the state of the section planes
    * currently applied to the viewer by this extension.
    * 
    * @param {THREE.Vector3|Object} [ignoreGlobalOffset=false]
    * 
    * @returns {Object|null}
    */
proto.getViewValues = function (ignoreGlobalOffset) {

  var boxValues = this.tool.getSectionBoxValues(ignoreGlobalOffset);
  if (boxValues)
  return boxValues;

  var planeValues = this.tool.getSectionPlaneValues(ignoreGlobalOffset);
  if (planeValues)
  return planeValues;

  return null;
};

/**
    * @private
    */
proto.onToolbarCreated = function () {
  if (this.onToolbarCreatedBinded) {
    this.viewer.removeEventListener(Autodesk.Viewing.TOOLBAR_CREATED_EVENT, this.onToolbarCreatedBinded);
    this.onToolbarCreatedBinded = null;
  }
  this.createUI();
};

/**
    * @private
    */
proto.onReset = function () {
  this.tool.resetSection();
};

/**
    * @private
    */
proto.onViewerSetView = function () /*event*/{
  this.deactivate();
};

/***
    * @private
    */
proto.createUI = function ()
{
  var viewer = this.viewer;
  var AVU = Autodesk.Viewing.UI;

  this.sectionToolButton = new AVU.ComboButton("toolbar-sectionTool");
  this.sectionToolButton.setToolTip('Section analysis');
  this.sectionToolButton.setIcon("adsk-icon-section-analysis");
  this.createSubmenu(this.sectionToolButton);

  // make sure inspect tools is visible
  var toolbar = viewer.getToolbar(false);
  var modelTools = toolbar.getControl(Autodesk.Viewing.TOOLBAR.MODELTOOLSID);

  // place section tool before reset tool
  if (modelTools) {
    var resetTool = modelTools.getControl("toolbar-resetTool");
    if (resetTool) {
      modelTools.addControl(this.sectionToolButton, { index: modelTools.indexOf(resetTool.getId()) });
    } else {
      modelTools.addControl(this.sectionToolButton, { index: 0 });
    }
  }
};

/**
    *
    * @param parentButton
    * @private
    */
proto.createSubmenu = function (parentButton)
{
  var that = this;
  var viewer = this.viewer;
  var AVU = Autodesk.Viewing.UI;

  function createNavToggler(button, name) {
    return function () {
      var state = button.getState();
      var enable = function enable() {
        if (button instanceof AVU.ComboButton === false) {
          that.activate(name);
        } else {
          that.enableSectionTool(true);
          that.tool.attachControl(true);
        }
      };

      if (state === AVU.Button.State.INACTIVE) {
        button.setState(AVU.Button.State.ACTIVE);
        // Long initialization may cause issues on touch enabled devices, make it async
        if (Autodesk.Viewing.isMobileDevice()) {
          setTimeout(enable, 1);
        } else {
          enable();
        }
      } else if (state === AVU.Button.State.ACTIVE) {
        button.setState(AVU.Button.State.INACTIVE);
        that.deactivate();
      }
      that.sectionStyle = name;
    };
  }

  function updateSectionButtons() {
    var areVectorsEqual = function () {
      var v = new THREE.Vector3();
      return function (a, b, sqtol) {
        v.subVectors(a, b);
        return v.lengthSq() < sqtol;
      };
    }();

    var unitx = new THREE.Vector3(1, 0, 0);
    var unity = new THREE.Vector3(0, 1, 0);
    var unitz = new THREE.Vector3(0, 0, 1);
    var right = viewer.autocam.getWorldRightVector();
    var up = viewer.autocam.getWorldUpVector();
    var front = viewer.autocam.getWorldFrontVector();

    var tol = 0.0001;
    if (areVectorsEqual(up, unitx, tol)) {
      that.sectionYButton.setIcon("adsk-icon-plane-x");
    } else if (areVectorsEqual(up, unitz, tol)) {
      that.sectionYButton.setIcon("adsk-icon-plane-z");
    } else {
      that.sectionYButton.setIcon("adsk-icon-plane-y");
    }

    if (areVectorsEqual(right, unity, tol)) {
      that.sectionXButton.setIcon("adsk-icon-plane-y");
    } else if (areVectorsEqual(right, unitz, tol)) {
      that.sectionXButton.setIcon("adsk-icon-plane-z");
    } else {
      that.sectionXButton.setIcon("adsk-icon-plane-x");
    }

    if (areVectorsEqual(front, unitx, tol)) {
      that.sectionZButton.setIcon("adsk-icon-plane-x");
    } else if (areVectorsEqual(front, unity, tol)) {
      that.sectionZButton.setIcon("adsk-icon-plane-y");
    } else {
      that.sectionZButton.setIcon("adsk-icon-plane-z");
    }

    viewer.removeEventListener(Autodesk.Viewing.GEOMETRY_LOADED_EVENT, updateSectionButtons);
  }

  var sectionXButton = this.sectionXButton = new AVU.Button("toolbar-sectionTool-x");
  sectionXButton.setToolTip('Add X plane');
  sectionXButton.setIcon("adsk-icon-plane-x");
  sectionXButton.onClick = createNavToggler(sectionXButton, 'x');
  parentButton.addControl(sectionXButton);

  var sectionYButton = this.sectionYButton = new AVU.Button("toolbar-sectionTool-y");
  sectionYButton.setToolTip('Add Y plane');
  sectionYButton.setIcon("adsk-icon-plane-y");
  sectionYButton.onClick = createNavToggler(sectionYButton, 'y');
  parentButton.addControl(sectionYButton);

  var sectionZButton = this.sectionZButton = new AVU.Button("toolbar-sectionTool-z");
  sectionZButton.setToolTip('Add Z plane');
  sectionZButton.setIcon("adsk-icon-plane-z");
  sectionZButton.onClick = createNavToggler(sectionZButton, 'z');
  parentButton.addControl(sectionZButton);

  var sectionBoxButton = this.sectionBoxButton = new AVU.Button("toolbar-sectionTool-box");
  sectionBoxButton.setToolTip('Add box');
  sectionBoxButton.setIcon("adsk-icon-box");
  sectionBoxButton.onClick = createNavToggler(sectionBoxButton, 'box');
  parentButton.addControl(sectionBoxButton);

  viewer.addEventListener(Autodesk.Viewing.GEOMETRY_LOADED_EVENT, updateSectionButtons);
};

/**
    * @private
    */
proto.destroyUI = function ()
{
  var viewer = this.viewer;

  var toolbar = viewer.getToolbar && viewer.getToolbar(false);
  if (toolbar) {
    var modelTools = toolbar.getControl(Autodesk.Viewing.TOOLBAR.MODELTOOLSID);
    if (modelTools && this.sectionToolButton) {
      var inspectSubmenu = modelTools.getControl("toolbar-inspectSubMenu");
      if (inspectSubmenu) {
        inspectSubmenu.removeControl(this.sectionToolButton.getId());
      } else {
        modelTools.removeControl(this.sectionToolButton.getId());
      }
      this.sectionToolButton = null;
    }
  }
};

/**
    * Activates a section plane for user to interact with.
    * It performs the same action as the UI button.
    * 
    * @param {string} mode - Accepted values are 'x', 'y', 'z' and 'box' (in lowercase)
    * @returns {boolean} - true if the activation was successful.
    * @alias Autodesk.Viewing.Extensions.SectionExtension#activate
    */
proto.activate = function (mode) {
  if (this.activeStatus && this.mode === mode) {
    return;
  }
  this.enableSectionTool(true);
  switch (mode) {
    default:
    case 'x':
      this.tool.setSection('X');
      this.mode = 'x';
      break;
    case 'y':
      this.tool.setSection('Y');
      this.mode = 'y';
      break;
    case 'z':
      this.tool.setSection('Z');
      this.mode = 'z';
      break;
    case 'box':
      this.tool.setSection('BOX');
      this.mode = 'box';
      break;}

  this.activeStatus = true;
  return true;
};

/**
    * Removes the section plane/box from the 3D canvas.
    * @alias Autodesk.Viewing.Extensions.SectionExtension#deactivate
    */
proto.deactivate = function () {
  if (this.activeStatus) {
    this.enableSectionTool(false);
    this.activeStatus = false;
  }
  return true;
};

Autodesk.Viewing.theExtensionManager.registerExtension('Autodesk.Section', SectionExtension);

/***/ }),

/***/ "./extensions/Section/SectionTool.js":
/*!*******************************************!*\
  !*** ./extensions/Section/SectionTool.js ***!
  \*******************************************/
/*! exports provided: SectionTool */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SectionTool", function() { return SectionTool; });
/* harmony import */ var _CompGeom_x_mesh_plane__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../CompGeom/x-mesh-plane */ "./extensions/CompGeom/x-mesh-plane.js");
/* harmony import */ var _CompGeom_x_box_plane__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../CompGeom/x-box-plane */ "./extensions/CompGeom/x-box-plane.js");
/* harmony import */ var _CompGeom_edge_set__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../CompGeom/edge-set */ "./extensions/CompGeom/edge-set.js");
/* harmony import */ var _thirdparty_three_js_TransformControls__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../thirdparty/three.js/TransformControls */ "./thirdparty/three.js/TransformControls.js");





//import { DCEL } from "../CompGeom/dcel";


// Declared at the bottom, inside a function.
var SectionMeshClass = null;

/**
                              * Tool that provides visual controls for the user to change the cutplane's position and angle.
                              * It can (and should) be hooked to [ToolController's registerTool]{@Autodesk.Viewing.ToolController#registerTool}
                              *
                              * @param {Autodesk.Viewing.Viewer3D} viewer - Viewer3D instance
                              * @param {Object} config - Configuration values
                              * @param {Object} options.tintColor - Object containing attributes r, g, b in the range [0..1]
                              * @param {Number} options.tintIntensity - Value range [0..1]
                              * @constructor
                              */
var SectionTool = function SectionTool(viewer, options)
{
  var _viewer = viewer.impl;

  var _names = ["section"];
  var _active = false;

  var _isDragging = false;
  var _boxChanged = false;
  var _isPlaneOn = true;
  var _tintColor = options.tintColor;
  var _tintIntensity = options.tintIntensity;

  var _transRotControl;
  var _transControl;
  var _sectionGroups = [];
  var _sectionPlanes = [];
  var _sectionPicker = [];
  var _activeMode = "";
  var _overlayName = "gizmo";
  var _touchType = null;
  var _initialized = false;
  var _visibleAtFirst = true;
  var _outlineIndices = [[0, 1], [1, 3], [3, 2], [2, 0]];
  var _priority = 70;

  Object(_thirdparty_three_js_TransformControls__WEBPACK_IMPORTED_MODULE_3__["init_TransformGizmos"])();
  init_SectionMesh();

  function initControl() {

    if (_initialized)
    return;

    _transRotControl = new THREE.TransformControls(_viewer.camera, _viewer.canvas, "transrotate");
    _transRotControl.addEventListener('change', updateViewer);
    _transRotControl.setSnap(Math.PI / 2, Math.PI / 36); // snap to 90 degs within 5 degs range 

    _transControl = new THREE.TransformControls(_viewer.camera, _viewer.canvas, "translate");
    _transControl.addEventListener('change', updateViewer);

    // add to overlay scene
    if (_viewer.overlayScenes[_overlayName] === undefined) {
      _viewer.createOverlayScene(_overlayName);
    }
    _viewer.addOverlay(_overlayName, _transRotControl);
    _viewer.addOverlay(_overlayName, _transControl);

    viewer.addEventListener(Autodesk.Viewing.CAMERA_CHANGE_EVENT, updateControls);
    viewer.addEventListener(Autodesk.Viewing.ISOLATE_EVENT, updateSections);
    viewer.addEventListener(Autodesk.Viewing.HIDE_EVENT, updateSections);
    viewer.addEventListener(Autodesk.Viewing.SHOW_EVENT, updateSections);
    viewer.addEventListener(Autodesk.Viewing.FRAGMENTS_LOADED_EVENT, fragmentsLoaded);

    // consider cutplane changes of other tools, so that cap meshes consider them too
    viewer.addEventListener(Autodesk.Viewing.CUTPLANES_CHANGE_EVENT, cutPlanesChanged);

    _initialized = true;
  }

  function deinitControl() {

    if (!_initialized)
    return;

    _viewer.removeOverlay(_overlayName, _transRotControl);
    _transRotControl.removeEventListener('change', updateViewer);
    _transRotControl = null;
    _viewer.removeOverlay(_overlayName, _transControl);
    _transControl.removeEventListener('change', updateViewer);
    _transControl = null;
    _viewer.removeOverlayScene(_overlayName);

    viewer.removeEventListener(Autodesk.Viewing.CAMERA_CHANGE_EVENT, updateControls);
    viewer.removeEventListener(Autodesk.Viewing.ISOLATE_EVENT, updateSections);
    viewer.removeEventListener(Autodesk.Viewing.HIDE_EVENT, updateSections);
    viewer.removeEventListener(Autodesk.Viewing.SHOW_EVENT, updateSections);
    viewer.removeEventListener(Autodesk.Viewing.FRAGMENTS_LOADED_EVENT, fragmentsLoaded);

    _initialized = false;
  }

  function updateViewer() {
    _viewer.invalidate(false, false, true);
  }

  function updateControls() {
    if (_transRotControl) {
      _transRotControl.update();
    }
    if (_transControl) {
      _transControl.update();
    }
  }

  function updateSections() {
    if (_active && _sectionPlanes.length === 1) {
      updatePlaneMeshes(true);
      updateControls();
      updateCapMeshes(new THREE.Plane().setComponents(_sectionPlanes[0].x, _sectionPlanes[0].y, _sectionPlanes[0].z, _sectionPlanes[0].w));
    }
  }

  /*function mix(a, b, val) {
        return a * (1.0 - val) + b * val;
    }*/

  function getDiffuseColor(material) {
    return material && material.color || new THREE.Color(0xffffff);
  }

  /*function getSpecularColor(material) {
        return (material && material.specular) || new THREE.Color(0xffffff);
    }
     function tintColor(c) {
        var intensity = Autodesk.Viewing.Extensions.Section.tintIntensity;
        var tc = _tintColor;
        c.r = mix(c.r, tc.r, intensity);
        c.g = mix(c.g, tc.g, intensity);
        c.b = mix(c.b, tc.b, intensity);
    }*/


  // Object used to iterator all fragments in all dbids in all models in a scene
  // _timeSlice is the time in milliseconds before the iterator will allow itself
  // to be interrupted. _sliceDelay is the time in milliseconds the iterator delays
  // before starting a new time slice. The default values are 15 and 0 respectively.
  // I did some experiments and it seemed like these values worked pretty well.
  // _sliceDelay doesn't seem to matter very much, but making _timeSlice much
  // larger will cause highlights to look jerky.
  function FragmentIterator(_timeSlice, _sliceDelay) {

    var _capTimer = 0; // Timer used to delay time slices
    var _callback; // Callback for each fragment
    var _models; // Array of models in the scene
    var _curModel; // Current model
    var _curLoadedObj; // On demand loaded dbIds for current model
    var _dbIds = []; // Database ids for the current model
    var _fragIds = []; // Fragment ids for the current database id
    var _fragmentMap; // Fragment map for current model
    var _m; // Current index in _models
    var _d; // Current index in _dbIds
    var _f; // Current index in _fragIds
    var _loadedModels = []; // Models and dbIds loaded after the section has started

    // Default value for _timeSlize and _sliceDelay
    _timeSlice = _timeSlice || 15;
    _sliceDelay = _sliceDelay || 0;

    // Start the iterator
    // models is the array of models to iterate
    // delay is a delay to start the iteration. < 0 starts without any delay
    // callback is the callback for each fragment:
    //   callback(fragId, dbId, model, lastFrag, fragLoaded)
    // lastFrag is a boolean that is true when fragId is the last fragment for dbId.
    // fragLoaded is a boolean that is true when the dbId was loaded on demand.
    this.start = function (models, delay, callback) {
      reset(models);

      _callback = callback;
      if (callback) {
        if (delay >= 0)
        _capTimer = setTimeout(doIteration, delay);else

        doIteration();
      }
    };

    // Signal that a list of dbIds has been loaded from a model
    this.loadedDbIds = function (model, fragIds) {
      if (!_callback || !model || !fragIds || fragIds.length <= 0)
      return;

      var loadedObj = _loadedModels.find(function (obj) {
        return obj.model === model;
      });
      if (!loadedObj) {
        loadedObj = { model: model, dbIds: {} };
        _loadedModels.push(loadedObj);
        if (model === _curModel)
        _curLoadedObj = loadedObj;
      }

      var frags = model.getFragmentList();

      // Convert fragIds to the dbIds that they are part of
      fragIds.forEach(function (fragId) {
        var dbId = frags.getDbIds(fragId) | 0; // Only 2d has multiple dbIds per fragId
        loadedObj.dbIds[dbId] = true;
      });

      // If we aren't generating caps, then start it.
      if (_capTimer == 0)
      _capTimer = setTimeout(doIteration, _sliceDelay);
    };

    // Reset the iterator, this is so we can clear the manager at the end.
    function reset(models) {
      if (_capTimer)
      clearTimeout(_capTimer);
      _capTimer = 0;
      _models = models;
      _dbIds.length = 0;
      _fragIds.length = 0;
      _fragmentMap = null;
      _loadedModels.length = 0;
      _m = -1;
      _d = 0;
      _f = 0;
    }

    // Do a single time slice
    function doIteration() {
      _capTimer = 0;
      var endTime = performance.now() + _timeSlice;
      while (performance.now() < endTime) {
        // If we are done, then return
        if (!next()) {
          // The cap scene is in sceneAfter, so we need to redraw the model to see the caps.
          // LMV-2571 - clear the render, as otherwise we will draw transparent objects atop themselves.
          _viewer.invalidate(true, true);
          // Clear everything when we are done
          reset(null);
          return;
        }

        var dbId = _dbIds[_d];
        if (_f == 0 && _curLoadedObj)
        delete _curLoadedObj.dbIds[dbId];

        // Call the call back function
        _callback(_fragIds[_f], dbId, _curModel, _f + 1 >= _fragIds.length, !_models || _m >= _models.length);
      }

      // Schedule the next time slice
      _capTimer = setTimeout(doIteration, _sliceDelay);
    }

    // Advance to the next model in _models
    function nextModel() {
      // Continue processing the next model in _models
      if (_models && _m < _models.length) {
        // Go to next model
        while (++_m < _models.length) {
          _fragmentMap = _models[_m].getFragmentMap();
          // Only process the model, if it has a fragment map
          if (_fragmentMap) {
            // Get the list of dbIds.
            _dbIds.length = 0;
            _fragmentMap.enumNodeChildren(_models[_m].getRootId(), function (dbId) {
              _dbIds.push(dbId);
            }, true);
            // Only process the model if we got some ids
            if (_dbIds.length > 0) {
              // Set the current model and newly loaded dbIds
              _curModel = _models[_m];
              _curLoadedObj = _loadedModels.find(function (obj) {
                return obj.model === _curModel;
              });
              return _curModel;
            }
          }
        }
      }

      for (var i = 0; i < _loadedModels.length; ++i) {
        // Get the next model with newly loaded dbIds
        _curModel = _loadedModels[i].model;
        _fragmentMap = _curModel.getFragmentMap();
        // Only process the model, if it has a fragment map
        if (_fragmentMap) {
          // Get the list of dbIds.
          _dbIds.length = 0;
          var dbIdStr;
          for (dbIdStr in _loadedModels[i].dbIds) {
            var dbId = Number(dbIdStr);
            if (isFinite(dbId))
            _dbIds.push(dbId);
          }
          // Only process the model if we got some ids
          if (_dbIds.length > 0) {
            _loadedModels.splice(0, i + 1); // Removed models processed and skipped
            _curLoadedObj = null; // No newly loaded dbIds, they are being processed
            return true;
          }
        }
      }

      // Done clear the current model and new loaded dbIds
      _curModel = null;
      _curLoadedObj = null;
      _loadedModels.length = 0; // No more models, make sure list is empty

      // End of the models
      return false;
    }

    // Advance to the next database id
    function nextDbId() {
      // At the end, return false
      if (_d >= _dbIds.length)
      return false;

      // Go to next database id
      while (++_d < _dbIds.length) {
        var dbId = _dbIds[_d];
        // Only process dbIds that are not hidden and not off
        if (!_fragmentMap.isNodeHidden(dbId) && !_fragmentMap.isNodeOff(dbId)) {
          //All fragments that belong to the same node make part of the
          //same object so we have to accumulate all their intersections into one list
          _fragIds.length = 0;
          _fragmentMap.enumNodeFragments(dbId, function (fragId) {
            _fragIds.push(fragId);
          }, false);
          // Only process the database id if there are some fragments
          if (_fragIds.length > 0)
          return true;
        }
      }

      // end of the database ids
      return false;
    }

    // Advance to the next fragment
    function next() {
      // If we are not a the end of the fragment list, then process it
      if (++_f < _fragIds.length)
      return true;

      // Start the fragment list at the beginning
      _f = 0;
      for (;;) {
        // If we have more database ids, then process them
        if (nextDbId())
        return true;
        // If we don't have another model, then we are done
        if (!nextModel())
        return false;
        // restart the database ids for the new model
        _d = -1;
      }
    }
  }

  // Use the same fragment iterator for all fragments
  var _fragIterator = new FragmentIterator();

  function updateCapMeshes(plane) {

    var oldsection = _viewer.sceneAfter.getObjectByName("section");
    if (oldsection)
    _viewer.sceneAfter.remove(oldsection);

    var section = new THREE.Object3D();
    section.name = "section";
    _viewer.sceneAfter.add(section);

    var section3D = new THREE.Object3D();
    section.add(section3D);
    var section2D = new THREE.Object3D();
    section.add(section2D);

    var avp = Autodesk.Viewing.Private;


    var toPlaneCoords = Object(_CompGeom_x_mesh_plane__WEBPACK_IMPORTED_MODULE_0__["makePlaneBasis"])(plane);
    var fromPaneCoords = new THREE.Matrix4().getInverse(toPlaneCoords);

    var mat2dname = _viewer.matman().create2DMaterial(null, { skipCircles: true, skipEllipticals: true, isScreenSpace: true }, false, false);
    var mat2d = _viewer.matman().findMaterial(null, mat2dname);
    mat2d.transparent = true;
    mat2d.depthTest = true;
    mat2d.polygonOffset = true;
    mat2d.polygonOffsetFactor = -1;
    mat2d.polygonOffsetUnits = 0.1; // 1.0 is usually way too high, see LMV-1072
    mat2d.cutplanes = _otherCutPlanes; // make sure that cap meshes respect cutplanes from other tools

    var box = new THREE.Box3();

    //var worldBox = _viewer.getVisibleBounds(true);   

    //some heuristic for line width of the section outline based on model size.
    //half a percent of the model size is what we do here.
    //var lineWidth = 0.5 * 5e-5 * worldBox.size().length();

    var models = _viewer.modelQueue().getModels();

    var intersects = [];
    var material;

    function removeMesh(object, modelId, dbId, disposeMaterial) {
      var child = object.children.find(function (mesh) {
        return mesh.modelId == modelId && mesh.dbId == dbId;
      });
      if (child) {
        object.remove(child);
        if (child.geometry)
        child.geometry.dispose();
        if (disposeMaterial && child.material)
        child.material.dispose();
      }
    }

    // Start iterating the fragments
    _fragIterator.start(models, 50, function (fragId, dbId, model, lastFrag, fragLoaded) {

      // Collect intersections for this fragment
      var frags = model.getFragmentList();
      frags.getWorldBounds(fragId, box);
      if (Object(_CompGeom_x_box_plane__WEBPACK_IMPORTED_MODULE_1__["xBoxPlane"])(plane, box)) {
        var m = frags.getVizmesh(fragId);

        if (m.geometry && !m.geometry.is2d && !m.geometry.isLines && m.material.cutplanes) {
          material = m.material;
          Object(_CompGeom_x_mesh_plane__WEBPACK_IMPORTED_MODULE_0__["xMeshPlane"])(plane, m, intersects);
        }
      }

      // If this is the last fragment for dbId, process the intersections
      if (lastFrag) {
        if (intersects.length) {

          var bbox = new THREE.Box3();
          Object(_CompGeom_x_mesh_plane__WEBPACK_IMPORTED_MODULE_0__["convertToPlaneCoords"])(toPlaneCoords, intersects, bbox);

          //Create the 2D line geometry
          var vbb = new avp.VertexBufferBuilder(false, 8 * intersects.length);

          var color = getDiffuseColor(material);
          var r = 0 | color.r * 0.25 * 255.5;
          var g = 0 | color.g * 0.25 * 255.5;
          var b = 0 | color.b * 0.25 * 255.5;

          var c = 0xff000000 | b << 16 | g << 8 | r;


          var eset = new _CompGeom_edge_set__WEBPACK_IMPORTED_MODULE_2__["EdgeSet"](intersects, bbox, bbox.size().length() * 1e-6);
          eset.snapEdges();
          eset.sanitizeEdges();
          eset.stitchContours();

          //Create the 3D mesh
          var cset = eset.triangulate();

          //Testing code path using the fancier DCEL structure for triangulation of the section edges.
          //TODO: compare quality and performance before switching to this and retiring the EdgeSet code path
          /*
                              var dcel = new DCEL(bbox, bbox.size().length() * 1e-6);
                              intersects.forEach(e => {
                                  dcel.addDirectedEdge(e.v1.x, e.v1.y, e.v2.x, e.v2.y, true, dbId);
                              });
          
                              dcel.finalize(true);
          
                              let cset = dcel.closedAreas[0];
          */

          if (cset) {
            /*
                                         if (cset.triangulationFailed)
                                             c = 0xffffff00;
                     */

            for (var j = 0; j < cset.contours.length; j++) {

              var cntr = cset.contours[j];

              for (var k = 1; k < cntr.length; k++) {
                var pt1 = cset.pts[cntr[k - 1]];
                var pt2 = cset.pts[cntr[k]];
                vbb.addSegment(pt1.x, pt1.y, pt2.x, pt2.y, 0, -2.0, /*isClosed ? c : rc*/c, dbId, 0);
              }

            }


            var mdata = { mesh: vbb.toMesh() };

            avp.BufferGeometryUtils.meshToGeometry(mdata);

            var bg2d = mdata.geometry;
            bg2d.streamingDraw = true;
            bg2d.streamingIndex = true;

            // If this cap was from geometry loaded on demand, then
            // remove any geometry that might already by in the scene
            if (fragLoaded)
            removeMesh(section2D, model.id, dbId, false);

            var mesh2d = new THREE.Mesh(bg2d, mat2d);

            mesh2d.matrix.copy(fromPaneCoords);
            mesh2d.matrixAutoUpdate = false;
            mesh2d.frustumCulled = false;
            mesh2d.modelId = model.id; // So we can look it up later
            mesh2d.dbId = dbId;
            section2D.add(mesh2d);

            //Create triangulated capping polygon
            {
              if (!cset.triangulationFailed) {

                var bg = cset.toPolygonMesh(material.packedNormals);

                var mat = _viewer.matman().cloneMaterial(material, model);

                mat.packedNormals = material.packedNormals;
                mat.cutplanes = _otherCutPlanes; // make sure that cap meshes respect cutplanes from other tools
                mat.side = THREE.FrontSide;
                mat.depthTest = true;
                mat.map = null;
                mat.bumpMap = null;
                mat.normalMap = null;
                mat.alphaMap = null;
                mat.specularMap = null;
                mat.transparent = false;
                mat.depthWrite = true;
                mat.hatchPattern = true;
                mat.needsUpdate = true;

                var angle = (material.id + 2) * Math.PI * 0.125;
                var tan = Math.tan(angle);
                mat.hatchParams = new THREE.Vector2(tan, 10.0);
                mat.hatchTintColor = _tintColor;
                mat.hatchTintIntensity = _tintIntensity;

                // If the material is prism, clear all the map definitions.
                if (mat.prismType != null) {
                  mat.defines = {};
                  mat.defines[mat.prismType.toUpperCase()] = "";
                  if (mat.prismType == "PrismWood") {
                    mat.defines["NO_UVW"] = "";
                  }
                }

                // If this cap was from geometry loaded on demand, then
                // remove any geometry that might already by in the scene
                if (fragLoaded)
                removeMesh(section3D, model.id, dbId, true);

                var capmesh = new THREE.Mesh(bg, mat);
                capmesh.matrix.copy(fromPaneCoords);
                capmesh.matrixAutoUpdate = false;
                capmesh.modelId = model.id; // So we can look it up later
                capmesh.dbId = dbId;
                capmesh.fragId = intersects.fragId;

                section3D.add(capmesh);
              }

            }

          }
        }

        // Clear intersections for the next dbId
        intersects.length = 0;
      } // last Fragment for dbId

    }); //_fragIterator.start

  }

  // We use an own cut plane set to distinguish our own cut planes from others.
  var _ownCutPlaneSet = 'Autodesk.Viewing.Extension.Section.SectionTool';

  // Make sure that the viewer always uses the SectionTool's plane to adjust 2D rendering resolution.
  _viewer.setCutPlaneSetFor2DRendering(_ownCutPlaneSet);

  // Keep track of cutplanes that are not our own, because we have to apply them to our cap meshes
  var _otherCutPlanes = [];

  // Trigger update of cap mesh materials if number of cutplanes have changed by other tools
  function updateCapMaterials() {

    // apply cutplanes to all active cap meshes
    var section = _viewer.sceneAfter.getObjectByName("section");
    if (!section) {
      return;
    }

    section.traverse(function (obj) {
      // we only care for THREE.Mesh with material
      if (!obj instanceof THREE.Mesh || !obj.material) {
        return;
      }
      obj.material.needsUpdate = true;
    });
  }

  // Called by viewer if any cutplanes are modified. It makes sure that cutplanes controlled by separate tools
  // (with own cutplane sets) are considered by our cap meshes.
  function cutPlanesChanged(e) {

    var numCutPlanesBefore = _otherCutPlanes.length;

    // Collect all active cutplanes from other tools
    //
    // NOTE: It's essential that we don't create a new array, but just refill the same one.
    //       Since the cap meshes are created async, the cutPlaneChange event may come in the middle of
    //       the cap mesh generation. For consistency, we want all cap meshes to share the same cutplane array.
    _otherCutPlanes.length = 0;
    var cpSets = _viewer.getCutPlaneSets();
    for (var i = 0; i < cpSets.length; i++) {

      // skip our own cut planes
      var cpName = cpSets[i];
      if (cpName === _ownCutPlaneSet) {
        continue;
      }

      // add cutplanes of this set
      var cp = _viewer.getCutPlaneSet(cpName);
      for (var j = 0; j < cp.length; j++) {
        _otherCutPlanes.push(cp[j]);
      }
    }

    // If the number of cutplanes changed, this requires a shader recompile of the cap materials
    if (numCutPlanesBefore !== _otherCutPlanes.length) {
      updateCapMaterials();
    }
  }

  // Handle FRAGMENTS_LOADED_EVENT
  function fragmentsLoaded(e) {
    if (_active && _sectionPlanes.length === 1)
    _fragIterator.loadedDbIds(e.model, e.getFragIds());
  }

  function createPlaneMesh(plane, bbox) {
    var quat = new THREE.Quaternion().setFromUnitVectors(new THREE.Vector3(0, 0, 1), plane.normal);
    var geometry;
    var size;

    if (bbox) {
      // project bbox to set plane size
      var ptMax = plane.projectPoint(bbox.max);
      var ptMin = plane.projectPoint(bbox.min);
      var invQuat = quat.clone().inverse();
      ptMax.applyQuaternion(invQuat);
      ptMin.applyQuaternion(invQuat);
      size = new THREE.Vector3().subVectors(ptMax, ptMin);
      geometry = new THREE.PlaneBufferGeometry(size.x, size.y);
    } else {
      // project bounding sphere
      bbox = _viewer.getVisibleBounds();
      size = 2.0 * bbox.getBoundingSphere().radius;
      geometry = new THREE.PlaneBufferGeometry(size, size);
    }

    var material = new THREE.MeshBasicMaterial({
      opacity: 0,
      color: 0xffffff,
      side: THREE.DoubleSide,
      depthTest: false,
      depthWrite: false,
      transparent: true });


    var mesh = new SectionMeshClass(geometry, material, plane);
    var pt = plane.projectPoint(bbox.center());
    mesh.position.copy(pt);
    mesh.quaternion.multiply(quat);

    // add outlines with inverted background color
    var presetIndex = _viewer.currentLightPreset();
    presetIndex = Math.max(0, presetIndex);
    var bgColor = Autodesk.Viewing.Private.LightPresets[presetIndex].bgColorGradient;
    var color = "rgb(" + (255 - bgColor[0]) + "," + (255 - bgColor[1]) + "," + (255 - bgColor[2]) + ")";
    var lineMaterial = new THREE.LineBasicMaterial({ color: color, linewidth: 1, depthTest: false });
    var pos = mesh.geometry.getAttribute('position');
    for (var i = 0; i < _outlineIndices.length; i++) {
      geometry = new THREE.Geometry();
      geometry.vertices.push(new THREE.Vector3().fromArray(pos.array, _outlineIndices[i][0] * pos.itemSize),
      new THREE.Vector3().fromArray(pos.array, _outlineIndices[i][1] * pos.itemSize));
      var line = new THREE.Line(geometry, lineMaterial);
      mesh.add(line);
      mesh.outlines.push(line);
    }

    return mesh;
  }

  function updatePlaneMeshes(rebuild) {

    traverseSections(function (child) {
      if (child instanceof SectionMeshClass) {

        var i, line, pos, pt;
        if (child.connectivity.length > 0) {
          // section box
          var minv = new THREE.Matrix4().getInverse(child.matrixWorld);
          pt = new THREE.Vector3();
          pos = child.geometry.getAttribute('position');
          for (i = 0; i < pos.length / pos.itemSize; i++) {
            var connect = child.connectivity[i];
            if (intersectPlanes(child.plane, connect[0], connect[1], pt) !== null) {
              pt.applyMatrix4(minv);
              pos.setXYZ(i, pt.x, pt.y, pt.z);
            }
          }
          pos.needsUpdate = true;
          child.geometry.computeBoundingBox();
          child.geometry.computeBoundingSphere();

          for (i = 0; i < child.outlines.length; i++) {
            line = child.outlines[i];
            line.geometry.vertices[0].fromArray(pos.array, _outlineIndices[i][0] * pos.itemSize);
            line.geometry.vertices[1].fromArray(pos.array, _outlineIndices[i][1] * pos.itemSize);
            line.geometry.verticesNeedUpdate = true;
          }
        } else {
          // section plane
          if (rebuild) {
            var bbox = _viewer.getVisibleBounds();
            var size = 2.0 * bbox.getBoundingSphere().radius;
            pt = child.plane.projectPoint(bbox.center());
            child.geometry = new THREE.PlaneBufferGeometry(size, size);
            child.position.copy(pt);
            pos = child.geometry.getAttribute('position');
            for (i = 0; i < child.outlines.length; i++) {
              line = child.outlines[i];
              line.geometry.vertices[0].fromArray(pos.array, _outlineIndices[i][0] * pos.itemSize);
              line.geometry.vertices[1].fromArray(pos.array, _outlineIndices[i][1] * pos.itemSize);
              line.geometry.verticesNeedUpdate = true;
            }
          }
        }
      }
    });
  }

  function traverseSections(callback) {
    for (var i = 0; i < _sectionGroups.length; i++) {
      _sectionGroups[i].traverse(callback);
    }
  }

  function setSectionPlanes() {
    traverseSections(function (child) {
      if (child instanceof SectionMeshClass) {
        child.update();
      }
    });
    if (_sectionPlanes.length === 1) {
      updateCapMeshes(new THREE.Plane().setComponents(_sectionPlanes[0].x, _sectionPlanes[0].y, _sectionPlanes[0].z, _sectionPlanes[0].w));
    }
    _viewer.setCutPlaneSet(_ownCutPlaneSet, _sectionPlanes);
  }

  function showPlane(set) {
    for (var i = 0; i < _sectionGroups.length; i++) {
      _sectionGroups[i].visible = set;
    }

    if (_isPlaneOn !== set)
    updateViewer();

    _isPlaneOn = set;
  }

  function showSection(set) {
    if (set && _sectionPlanes.length > 0) {
      if (_sectionPlanes.length === 1) {
        updateCapMeshes(new THREE.Plane().setComponents(_sectionPlanes[0].x, _sectionPlanes[0].y, _sectionPlanes[0].z, _sectionPlanes[0].w));
      }
      _viewer.setCutPlaneSet(_ownCutPlaneSet, _sectionPlanes);
    }
    showPlane(set);
  }

  function attachControl(control, mesh) {
    control.attach(mesh);
    control.setPosition(mesh.position);
    control.visible = true;
  }

  function checkNormal(normal) {
    // flip normal if facing inward as eye direction
    var eyeVec = _viewer.api.navigation.getEyeVector();
    if (eyeVec.dot(normal) > 0) {
      normal.negate();
    }

    return normal;
  }

  function setPlane(normal, distance) {
    var obbox = _viewer.getVisibleBounds();
    var center = obbox.center();
    var group = new THREE.Group();
    // Calculate the plane signed distance using the dot product of the center point of the scene bounding box
    // and the normal vector.
    distance = distance !== undefined ? distance : -1 * center.dot(normal);
    var plane = new THREE.Plane(normal, distance);
    var mesh = createPlaneMesh(plane, null);
    group.add(mesh);
    _sectionPlanes.push(mesh.planeVec);
    _sectionGroups.push(group);
    _viewer.addOverlay(_overlayName, group);
    if (_transRotControl) {
      attachControl(_transRotControl, mesh);
      _transRotControl.showRotationGizmos(true);
      _sectionPicker = _transRotControl.getPicker();
    }
    setSectionPlanes();
  }

  function setBox(planeSet) {
    var normals = [
    new THREE.Vector3(1, 0, 0),
    new THREE.Vector3(0, 1, 0),
    new THREE.Vector3(0, 0, 1),
    new THREE.Vector3(-1, 0, 0),
    new THREE.Vector3(0, -1, 0),
    new THREE.Vector3(0, 0, -1)];


    var connectivities = [
    [[1, 2], [1, 5], [2, 4], [4, 5]], // 0
    [[3, 5], [0, 5], [2, 3], [0, 2]], // 1
    [[1, 3], [0, 1], [3, 4], [0, 4]], // 2
    [[1, 5], [1, 2], [4, 5], [2, 4]], // 3
    [[2, 3], [0, 2], [3, 5], [0, 5]], // 4
    [[0, 1], [3, 1], [0, 4], [3, 4]] // 5
    ];

    var group = new THREE.Group();
    var obbox = _viewer.getVisibleBounds();
    var center = obbox.center();
    var bbox = new THREE.Box3(obbox.min, center);

    // Initialize from planeSet ONLY if it's an AABB.
    var loadingBox = false;
    if (planeSet && planeSet.length === 6 && planeSet[0].x === 1.0) {

      // Assume that the order on planes is the same as in Array of normals defined above
      bbox = new THREE.Box3(
      new THREE.Vector3(planeSet[3].w, planeSet[4].w, planeSet[5].w),
      new THREE.Vector3(planeSet[0].w, planeSet[1].w, planeSet[2].w));

      center = bbox.max.clone();
      loadingBox = true;
    }

    ////center = obbox.max;   // Use this to initialize the box around the model
    ////bbox = obbox.clone(); // Use this to initialize the box around the model

    var planes = [],meshes = [];
    var i, mesh;
    for (i = 0; i < normals.length; i++) {

      if (loadingBox) {
        var plane = new THREE.Plane(normals[i], planeSet[i].w);
        planes.push(plane);
      } else

      {
        var plane = new THREE.Plane(normals[i], -1 * center.dot(normals[i]));
        planes.push(plane);

        // offset plane with negative normal to form an octant
        if (i > 2) {
          var ptMax = plane.orthoPoint(bbox.max);
          var ptMin = plane.orthoPoint(bbox.min);
          var size = new THREE.Vector3().subVectors(ptMax, ptMin);
          plane.constant -= size.length();
        }
      }

      mesh = createPlaneMesh(plane, bbox);
      group.add(mesh);
      meshes.push(mesh);
      _sectionPlanes.push(mesh.planeVec);
    }

    // build connectivity
    for (i = 0; i < meshes.length; i++) {
      mesh = meshes[i];
      var connectivity = connectivities[i];
      for (var j = 0; j < connectivity.length; j++) {
        var nc = [];
        var ct = connectivity[j];
        for (var k = 0; k < ct.length; k++) {
          nc.push(planes[ct[k]]);
        }
        mesh.connectivity.push(nc);
      }
    }

    _sectionGroups.push(group);
    _viewer.addOverlay(_overlayName, group);

    setSectionPlanes();
    updatePlaneMeshes();

    attachControl(_transRotControl, _sectionGroups[0].children[0]);
    attachControl(_transControl, _sectionGroups[0]);
    _transRotControl.showRotationGizmos(false);
    _sectionPicker = _transRotControl.getPicker().concat(_transControl.getPicker());
  }

  var intersectPlanes = function () {
    var m = new THREE.Matrix3();
    var n23 = new THREE.Vector3();
    var n31 = new THREE.Vector3();
    var n12 = new THREE.Vector3();
    return function (plane1, plane2, plane3, optionalTarget) {
      m.set(plane1.normal.x, plane1.normal.y, plane1.normal.z,
      plane2.normal.x, plane2.normal.y, plane2.normal.z,
      plane3.normal.x, plane3.normal.y, plane3.normal.z);

      var det = m.determinant();
      if (det === 0) return null;

      n23.crossVectors(plane2.normal, plane3.normal).multiplyScalar(-plane1.constant);
      n31.crossVectors(plane3.normal, plane1.normal).multiplyScalar(-plane2.constant);
      n12.crossVectors(plane1.normal, plane2.normal).multiplyScalar(-plane3.constant);

      var result = optionalTarget || new THREE.Vector3();
      return result.copy(n23).add(n31).add(n12).divideScalar(det);
    };
  }();

  var intersectObjects = function intersectObjects(pointer, objects, recursive) {
    return THREE.TransformControls.intersectObjects(pointer.canvasX, pointer.canvasY, objects, _viewer.camera, recursive);
  };

  // public functions

  /**
   * When active, the geometry will be sectioned by the current set cut plane.
   * @returns {boolean}
   */
  this.isActive = function () {
    return _active;
  };

  /**
      * Facilitates the initialization of a cut plane
      *
      * @param {String} name - Either 'X', 'Y', 'Z' or 'BOX'
      */
  this.setSection = function (name) {
    this.clearSection();
    var normal;
    _transRotControl.clientScale = 1;

    // Attempt to initialize the tool with a plane that is already set.
    var planeSet = _viewer.getCutPlaneSet('__set_view');
    if (planeSet && planeSet.length === 1 && name !== 'BOX') {
      name = 'SET_VIEW_PLANE';
    }

    switch (name) {
      case 'X':
        normal = new THREE.Vector3(1, 0, 0);
        setPlane(checkNormal(normal));
        break;
      case 'Y':
        normal = new THREE.Vector3(0, 1, 0);
        setPlane(checkNormal(normal));
        break;
      case 'Z':
        normal = new THREE.Vector3(0, 0, 1);
        setPlane(checkNormal(normal));
        break;
      case 'SET_VIEW_PLANE':
        var v4 = planeSet[0];
        normal = new THREE.Vector3(v4.x, v4.y, v4.z);
        setPlane(normal, v4.w);
        break;
      case 'BOX':
        setBox(planeSet);
        _transRotControl.clientScale = 2;
        this.recomputePivot();
        break;}

    _activeMode = name;

    // Clear sections from Viewer3D::setView
    _viewer.setCutPlaneSet('__set_view', undefined);
  };

  /**
      * Facilitates the initialization of a cut plane from a normal and distance
      *
      * @param {THREE.Vector4} normal (x,y,z) and distance (w)
      * @param {Number} distance
      */
  this.setSectionFromPlane = function (cutplane) {
    this.clearSection();
    setPlane(new THREE.Vector3(cutplane.x, cutplane.y, cutplane.z), cutplane.w);
    _activeMode = "";
  };

  /**
      * Removes any (and all) currently set cut plane(s).
      */
  this.clearSection = function () {

    if (_transRotControl)
    _transRotControl.detach();

    if (_transControl)
    _transControl.detach();

    // remove all sections
    while (_sectionPlanes.length > 0) {
      _sectionPlanes.pop();
    }

    while (_sectionGroups.length > 0) {
      var group = _sectionGroups.pop();
      _viewer.removeOverlay(_overlayName, group);
    }

    _fragIterator.start(null); // Shutdown iterator        
    var oldsection = _viewer.sceneAfter.getObjectByName("section");
    if (oldsection)
    _viewer.sceneAfter.remove(oldsection);

    _viewer.setCutPlaneSet(_ownCutPlaneSet);
  };

  this.isPlaneOn = function () {
    return _isPlaneOn;
  };

  this.showPlane = function (set) {
    showPlane(set);
  };

  /**
      * Whether translation and rotation controls are visible or not.
      * @param {Boolean} set
      */
  this.attachControl = function (set) {
    if (!_transRotControl || !_transControl) {
      return;
    }

    if (set) {
      attachControl(_transRotControl, _sectionGroups[0].children[0]);
      _transRotControl.highlight();
      if (_activeMode === 'BOX')
      attachControl(_transControl, _sectionGroups[0]);
    } else {
      _transRotControl.detach();
      _transControl.detach();
    }
  };

  /**
      * Invokes setSection with the last set of parameters used.
      */
  this.resetSection = function () {
    this.setSection(_activeMode);
  };

  // tool interface

  this.getNames = function () {
    return _names;
  };

  this.getName = function () {
    return _names[0];
  };

  this.register = function () {
  };

  this.deregister = function () {
    this.clearSection();
    deinitControl();
  };

  this.getPriority = function () {
    return _priority;
  };

  /**
      * [ToolInterface] Activates the tool
      * @param {String} name - unused
      */
  this.activate = function () /*name*/{

    initControl();

    _active = true;
    _isDragging = false;
    _visibleAtFirst = true;

    // keep only one section all the time per design
    _sectionPlanes = _sectionPlanes || [];

    showSection(true);
  };

  /**
      * [ToolInterface] Deactivates the tool
      * @param {String} name - unused
      */
  this.deactivate = function () /*name*/{
    _active = false;
    _isDragging = false;

    _fragIterator.start(null); // Shutdown iterator        
    var oldsection = _viewer.sceneAfter.getObjectByName("section");
    if (oldsection)
    _viewer.sceneAfter.remove(oldsection);


    showSection(false);
    _viewer.setCutPlaneSet(_ownCutPlaneSet);
    _transRotControl.detach();
    _transControl.detach();
  };

  this.update = function () /*highResTimestamp*/{
    return false;
  };

  this.handleSingleClick = function (event /*, button*/) {
    var pointer = event;
    var result = intersectObjects(pointer, _sectionGroups[0].children);
    if (result) {
      attachControl(_transRotControl, result.object);
      _transRotControl.highlight();
      updateViewer();
    }

    return false;
  };

  this.handleDoubleClick = function () /*event, button*/{
    return false;
  };

  this.handleSingleTap = function (event) {
    return this.handleSingleClick(event, 0);
  };

  this.handleDoubleTap = function () /*event*/{
    return false;
  };

  this.handleKeyDown = function () /*event, keyCode*/{
    return false;
  };

  this.handleKeyUp = function () /*event, keyCode*/{
    return false;
  };

  this.handleWheelInput = function () /*delta*/{
    return false;
  };

  this.handleButtonDown = function (event /*, button*/) {
    _isDragging = true;
    if (_transControl.onPointerDown(event))
    return true;
    return _transRotControl.onPointerDown(event);
  };

  this.handleButtonUp = function (event /*, button*/) {
    _isDragging = false;
    if (_boxChanged) {
      _boxChanged = false;
      this.recomputePivot();
    }
    if (_transControl.onPointerUp(event))
    return true;
    return _transRotControl.onPointerUp(event);
  };

  this.handleMouseMove = function (event) {
    if (_isDragging) {
      if (_transControl.onPointerMove(event)) {
        _boxChanged = true;
        setSectionPlanes();
        _transRotControl.update();
        return true;
      }
      if (_transRotControl.onPointerMove(event)) {
        _boxChanged = true;
        setSectionPlanes();
        updatePlaneMeshes();
        return true;
      }
    }

    _transControl.visible = _transControl.object !== undefined;

    if (event.pointerType !== 'touch') {
      var pointer = event;
      var result = intersectObjects(pointer, _sectionGroups[0].children);
      if (result) {
        _visibleAtFirst = false;
      }

      // show gizmo + plane when intersecting on non-touch 
      var visible = _visibleAtFirst || result || intersectObjects(pointer, _sectionPicker, true) ? true : false;
      _transRotControl.visible = visible;
      _transControl.visible = _transControl.visible && visible;
      showPlane(visible);
    }

    if (_transControl.onPointerHover(event))
    return true;

    return _transRotControl.onPointerHover(event);
  };

  this.handleGesture = function (event) {
    switch (event.type) {
      case "dragstart":
        _touchType = "drag";
        // Single touch, fake the mouse for now...
        return this.handleButtonDown(event, 0);

      case "dragmove":
        return _touchType === "drag" ? this.handleMouseMove(event) : false;

      case "dragend":
        if (_touchType === "drag") {
          _touchType = null;
          return this.handleButtonUp(event, 0);
        }
        return false;}

    return false;
  };

  this.handleBlur = function () /*event*/{
    return false;
  };

  this.handleResize = function () {
  };

  this.handlePressHold = function () /*event*/{
    return true;
  };

  this.recomputePivot = function () {

    var values = this.getSectionBoxValues(true);
    if (!values) return;

    var aabb = values.sectionBox;

    _viewer.api.navigation.setPivotPoint(new THREE.Vector3(
    aabb[0] + (aabb[3] - aabb[0]) * 0.5,
    aabb[1] + (aabb[4] - aabb[1]) * 0.5,
    aabb[2] + (aabb[5] - aabb[2]) * 0.5));

  };

  this.getSectionBoxValues = function (ignoreGlobalOffset) {

    var group = _sectionGroups[0];
    if (!group) {
      return null;
    }

    var planes = group.children;
    if (planes.length < 6) {
      return null;
    }

    var right = planes[0].position.x;
    var top = planes[1].position.y;
    var front = planes[2].position.z;
    var left = planes[3].position.x;
    var bttm = planes[4].position.y;
    var back = planes[5].position.z;

    var off = { x: 0, y: 0, z: 0 };
    if (!ignoreGlobalOffset) {
      off = _viewer.model.getData().globalOffset || off;
    }

    var aabb = [
    Math.min(left, right) + off.x,
    Math.min(top, bttm) + off.y,
    Math.min(front, back) + off.z,
    Math.max(left, right) + off.x,
    Math.max(top, bttm) + off.y,
    Math.max(front, back) + off.z];


    // Box doesn't support rotation at the moment.
    // Will have to take it into account if that becomes a feature.
    var transform = new THREE.Matrix4().identity().toArray();

    return {
      sectionBox: aabb,
      sectionBoxTransform: transform };

  };

  this.getSectionPlaneValues = function (ignoreGlobalOffset) {

    var group = _sectionGroups[0];
    if (!group) {
      return null;
    }

    var planes = group.children;
    if (planes.length !== 1) {
      return null;
    }

    var off = { x: 0, y: 0, z: 0 };
    if (!ignoreGlobalOffset) {
      off = _viewer.model.getData().globalOffset || off;
    }

    var plane = planes[0].plane;
    var constant = plane.constant - THREE.Vector3.prototype.dot.call(off, plane.normal);

    return {
      sectionPlane: [
      plane.normal.x,
      plane.normal.y,
      plane.normal.z,
      constant] };


  };
};

function init_SectionMesh() {

  if (SectionMeshClass)
  return;

  SectionMeshClass = function SectionMeshClass(geometry, material, plane) {

    THREE.Mesh.call(this, geometry, material, false);

    this.plane = plane;
    this.planeVec = new THREE.Vector4(plane.normal.x, plane.normal.y, plane.normal.z, plane.constant);
    this.connectivity = [];
    this.outlines = [];
  };

  SectionMeshClass.prototype = Object.create(THREE.Mesh.prototype);
  SectionMeshClass.prototype.constructor = SectionMeshClass;

  SectionMeshClass.prototype.update = function () {

    this.plane.normal.set(0, 0, 1);
    this.plane.normal.applyQuaternion(this.quaternion);

    var normal = this.plane.normal;
    var d = -1 * this.getWorldPosition().dot(normal);
    this.planeVec.set(normal.x, normal.y, normal.z, d);
    this.plane.constant = d;
  };

}

/***/ }),

/***/ "./node_modules/css-loader/index.js!./extensions/Section/Section.css":
/*!******************************************************************!*\
  !*** ./node_modules/css-loader!./extensions/Section/Section.css ***!
  \******************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

exports = module.exports = __webpack_require__(/*! ../../node_modules/css-loader/lib/css-base.js */ "./node_modules/css-loader/lib/css-base.js")(false);
// imports


// module
exports.push([module.i, "/**\n * Section CSS\n **/\n.sectionPanel {\n    line-height: 14px;\n    text-align: left;\n    z-index: 10;\n}\n\n.section-horizontal-divider\n{\n    height: 1px;\n    border-top: 1px solid rgba(200,200,200,0.8);\n}\n\n.section-submenu-select\n{\n    display: block;\n    position: relative;\n    width: calc(100% - 40px);\n    padding: 10px 20px 0px 20px;\n    opacity: 0.3;\n}\n\n.section-panel:hover .section-submenu-select {\n   opacity: 0.8;\n}\n\n.section-submenu-selectlabel\n{\n    position: relative;\n    display:  inline-block;\n    padding-right:  20px;\n    padding-bottom:  4px;\n}\n\n.section-restart {\n    margin: 10px 20px 15px 20px;\n    padding: 6px 10px 6px 10px;\n    width: calc(100% - 55px);\n    cursor: pointer;\n}\n\n.docking-panel:hover .section-restart {\n    background-color: rgba(255,255,255, .12);\n}\n\n.docking-panel .section-restart:hover {\n    background-color: rgba(166,194,255, 0.7);\n    -webkit-transition: all 0.2s ease;\n    -moz-transition: all 0.2s ease;\n    -ms-transition: all 0.2s ease;\n    -o-transition: all 0.2s ease;\n    transition: all 0.2s ease;\n}\n\n", ""]);

// exports


/***/ }),

/***/ "./node_modules/css-loader/lib/css-base.js":
/*!*************************************************!*\
  !*** ./node_modules/css-loader/lib/css-base.js ***!
  \*************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/
// css base code, injected by the css-loader
module.exports = function(useSourceMap) {
	var list = [];

	// return the list of modules as css string
	list.toString = function toString() {
		return this.map(function (item) {
			var content = cssWithMappingToString(item, useSourceMap);
			if(item[2]) {
				return "@media " + item[2] + "{" + content + "}";
			} else {
				return content;
			}
		}).join("");
	};

	// import a list of modules into the list
	list.i = function(modules, mediaQuery) {
		if(typeof modules === "string")
			modules = [[null, modules, ""]];
		var alreadyImportedModules = {};
		for(var i = 0; i < this.length; i++) {
			var id = this[i][0];
			if(typeof id === "number")
				alreadyImportedModules[id] = true;
		}
		for(i = 0; i < modules.length; i++) {
			var item = modules[i];
			// skip already imported module
			// this implementation is not 100% perfect for weird media query combinations
			//  when a module is imported multiple times with different media queries.
			//  I hope this will never occur (Hey this way we have smaller bundles)
			if(typeof item[0] !== "number" || !alreadyImportedModules[item[0]]) {
				if(mediaQuery && !item[2]) {
					item[2] = mediaQuery;
				} else if(mediaQuery) {
					item[2] = "(" + item[2] + ") and (" + mediaQuery + ")";
				}
				list.push(item);
			}
		}
	};
	return list;
};

function cssWithMappingToString(item, useSourceMap) {
	var content = item[1] || '';
	var cssMapping = item[3];
	if (!cssMapping) {
		return content;
	}

	if (useSourceMap && typeof btoa === 'function') {
		var sourceMapping = toComment(cssMapping);
		var sourceURLs = cssMapping.sources.map(function (source) {
			return '/*# sourceURL=' + cssMapping.sourceRoot + source + ' */'
		});

		return [content].concat(sourceURLs).concat([sourceMapping]).join('\n');
	}

	return [content].join('\n');
}

// Adapted from convert-source-map (MIT)
function toComment(sourceMap) {
	// eslint-disable-next-line no-undef
	var base64 = btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))));
	var data = 'sourceMappingURL=data:application/json;charset=utf-8;base64,' + base64;

	return '/*# ' + data + ' */';
}


/***/ }),

/***/ "./node_modules/style-loader/lib/addStyles.js":
/*!****************************************************!*\
  !*** ./node_modules/style-loader/lib/addStyles.js ***!
  \****************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/

var stylesInDom = {};

var	memoize = function (fn) {
	var memo;

	return function () {
		if (typeof memo === "undefined") memo = fn.apply(this, arguments);
		return memo;
	};
};

var isOldIE = memoize(function () {
	// Test for IE <= 9 as proposed by Browserhacks
	// @see http://browserhacks.com/#hack-e71d8692f65334173fee715c222cb805
	// Tests for existence of standard globals is to allow style-loader
	// to operate correctly into non-standard environments
	// @see https://github.com/webpack-contrib/style-loader/issues/177
	return window && document && document.all && !window.atob;
});

var getTarget = function (target, parent) {
  if (parent){
    return parent.querySelector(target);
  }
  return document.querySelector(target);
};

var getElement = (function (fn) {
	var memo = {};

	return function(target, parent) {
                // If passing function in options, then use it for resolve "head" element.
                // Useful for Shadow Root style i.e
                // {
                //   insertInto: function () { return document.querySelector("#foo").shadowRoot }
                // }
                if (typeof target === 'function') {
                        return target();
                }
                if (typeof memo[target] === "undefined") {
			var styleTarget = getTarget.call(this, target, parent);
			// Special case to return head of iframe instead of iframe itself
			if (window.HTMLIFrameElement && styleTarget instanceof window.HTMLIFrameElement) {
				try {
					// This will throw an exception if access to iframe is blocked
					// due to cross-origin restrictions
					styleTarget = styleTarget.contentDocument.head;
				} catch(e) {
					styleTarget = null;
				}
			}
			memo[target] = styleTarget;
		}
		return memo[target]
	};
})();

var singleton = null;
var	singletonCounter = 0;
var	stylesInsertedAtTop = [];

var	fixUrls = __webpack_require__(/*! ./urls */ "./node_modules/style-loader/lib/urls.js");

module.exports = function(list, options) {
	if (typeof DEBUG !== "undefined" && DEBUG) {
		if (typeof document !== "object") throw new Error("The style-loader cannot be used in a non-browser environment");
	}

	options = options || {};

	options.attrs = typeof options.attrs === "object" ? options.attrs : {};

	// Force single-tag solution on IE6-9, which has a hard limit on the # of <style>
	// tags it will allow on a page
	if (!options.singleton && typeof options.singleton !== "boolean") options.singleton = isOldIE();

	// By default, add <style> tags to the <head> element
        if (!options.insertInto) options.insertInto = "head";

	// By default, add <style> tags to the bottom of the target
	if (!options.insertAt) options.insertAt = "bottom";

	var styles = listToStyles(list, options);

	addStylesToDom(styles, options);

	return function update (newList) {
		var mayRemove = [];

		for (var i = 0; i < styles.length; i++) {
			var item = styles[i];
			var domStyle = stylesInDom[item.id];

			domStyle.refs--;
			mayRemove.push(domStyle);
		}

		if(newList) {
			var newStyles = listToStyles(newList, options);
			addStylesToDom(newStyles, options);
		}

		for (var i = 0; i < mayRemove.length; i++) {
			var domStyle = mayRemove[i];

			if(domStyle.refs === 0) {
				for (var j = 0; j < domStyle.parts.length; j++) domStyle.parts[j]();

				delete stylesInDom[domStyle.id];
			}
		}
	};
};

function addStylesToDom (styles, options) {
	for (var i = 0; i < styles.length; i++) {
		var item = styles[i];
		var domStyle = stylesInDom[item.id];

		if(domStyle) {
			domStyle.refs++;

			for(var j = 0; j < domStyle.parts.length; j++) {
				domStyle.parts[j](item.parts[j]);
			}

			for(; j < item.parts.length; j++) {
				domStyle.parts.push(addStyle(item.parts[j], options));
			}
		} else {
			var parts = [];

			for(var j = 0; j < item.parts.length; j++) {
				parts.push(addStyle(item.parts[j], options));
			}

			stylesInDom[item.id] = {id: item.id, refs: 1, parts: parts};
		}
	}
}

function listToStyles (list, options) {
	var styles = [];
	var newStyles = {};

	for (var i = 0; i < list.length; i++) {
		var item = list[i];
		var id = options.base ? item[0] + options.base : item[0];
		var css = item[1];
		var media = item[2];
		var sourceMap = item[3];
		var part = {css: css, media: media, sourceMap: sourceMap};

		if(!newStyles[id]) styles.push(newStyles[id] = {id: id, parts: [part]});
		else newStyles[id].parts.push(part);
	}

	return styles;
}

function insertStyleElement (options, style) {
	var target = getElement(options.insertInto)

	if (!target) {
		throw new Error("Couldn't find a style target. This probably means that the value for the 'insertInto' parameter is invalid.");
	}

	var lastStyleElementInsertedAtTop = stylesInsertedAtTop[stylesInsertedAtTop.length - 1];

	if (options.insertAt === "top") {
		if (!lastStyleElementInsertedAtTop) {
			target.insertBefore(style, target.firstChild);
		} else if (lastStyleElementInsertedAtTop.nextSibling) {
			target.insertBefore(style, lastStyleElementInsertedAtTop.nextSibling);
		} else {
			target.appendChild(style);
		}
		stylesInsertedAtTop.push(style);
	} else if (options.insertAt === "bottom") {
		target.appendChild(style);
	} else if (typeof options.insertAt === "object" && options.insertAt.before) {
		var nextSibling = getElement(options.insertAt.before, target);
		target.insertBefore(style, nextSibling);
	} else {
		throw new Error("[Style Loader]\n\n Invalid value for parameter 'insertAt' ('options.insertAt') found.\n Must be 'top', 'bottom', or Object.\n (https://github.com/webpack-contrib/style-loader#insertat)\n");
	}
}

function removeStyleElement (style) {
	if (style.parentNode === null) return false;
	style.parentNode.removeChild(style);

	var idx = stylesInsertedAtTop.indexOf(style);
	if(idx >= 0) {
		stylesInsertedAtTop.splice(idx, 1);
	}
}

function createStyleElement (options) {
	var style = document.createElement("style");

	if(options.attrs.type === undefined) {
		options.attrs.type = "text/css";
	}

	if(options.attrs.nonce === undefined) {
		var nonce = getNonce();
		if (nonce) {
			options.attrs.nonce = nonce;
		}
	}

	addAttrs(style, options.attrs);
	insertStyleElement(options, style);

	return style;
}

function createLinkElement (options) {
	var link = document.createElement("link");

	if(options.attrs.type === undefined) {
		options.attrs.type = "text/css";
	}
	options.attrs.rel = "stylesheet";

	addAttrs(link, options.attrs);
	insertStyleElement(options, link);

	return link;
}

function addAttrs (el, attrs) {
	Object.keys(attrs).forEach(function (key) {
		el.setAttribute(key, attrs[key]);
	});
}

function getNonce() {
	if (false) {}

	return __webpack_require__.nc;
}

function addStyle (obj, options) {
	var style, update, remove, result;

	// If a transform function was defined, run it on the css
	if (options.transform && obj.css) {
	    result = typeof options.transform === 'function'
		 ? options.transform(obj.css) 
		 : options.transform.default(obj.css);

	    if (result) {
	    	// If transform returns a value, use that instead of the original css.
	    	// This allows running runtime transformations on the css.
	    	obj.css = result;
	    } else {
	    	// If the transform function returns a falsy value, don't add this css.
	    	// This allows conditional loading of css
	    	return function() {
	    		// noop
	    	};
	    }
	}

	if (options.singleton) {
		var styleIndex = singletonCounter++;

		style = singleton || (singleton = createStyleElement(options));

		update = applyToSingletonTag.bind(null, style, styleIndex, false);
		remove = applyToSingletonTag.bind(null, style, styleIndex, true);

	} else if (
		obj.sourceMap &&
		typeof URL === "function" &&
		typeof URL.createObjectURL === "function" &&
		typeof URL.revokeObjectURL === "function" &&
		typeof Blob === "function" &&
		typeof btoa === "function"
	) {
		style = createLinkElement(options);
		update = updateLink.bind(null, style, options);
		remove = function () {
			removeStyleElement(style);

			if(style.href) URL.revokeObjectURL(style.href);
		};
	} else {
		style = createStyleElement(options);
		update = applyToTag.bind(null, style);
		remove = function () {
			removeStyleElement(style);
		};
	}

	update(obj);

	return function updateStyle (newObj) {
		if (newObj) {
			if (
				newObj.css === obj.css &&
				newObj.media === obj.media &&
				newObj.sourceMap === obj.sourceMap
			) {
				return;
			}

			update(obj = newObj);
		} else {
			remove();
		}
	};
}

var replaceText = (function () {
	var textStore = [];

	return function (index, replacement) {
		textStore[index] = replacement;

		return textStore.filter(Boolean).join('\n');
	};
})();

function applyToSingletonTag (style, index, remove, obj) {
	var css = remove ? "" : obj.css;

	if (style.styleSheet) {
		style.styleSheet.cssText = replaceText(index, css);
	} else {
		var cssNode = document.createTextNode(css);
		var childNodes = style.childNodes;

		if (childNodes[index]) style.removeChild(childNodes[index]);

		if (childNodes.length) {
			style.insertBefore(cssNode, childNodes[index]);
		} else {
			style.appendChild(cssNode);
		}
	}
}

function applyToTag (style, obj) {
	var css = obj.css;
	var media = obj.media;

	if(media) {
		style.setAttribute("media", media)
	}

	if(style.styleSheet) {
		style.styleSheet.cssText = css;
	} else {
		while(style.firstChild) {
			style.removeChild(style.firstChild);
		}

		style.appendChild(document.createTextNode(css));
	}
}

function updateLink (link, options, obj) {
	var css = obj.css;
	var sourceMap = obj.sourceMap;

	/*
		If convertToAbsoluteUrls isn't defined, but sourcemaps are enabled
		and there is no publicPath defined then lets turn convertToAbsoluteUrls
		on by default.  Otherwise default to the convertToAbsoluteUrls option
		directly
	*/
	var autoFixUrls = options.convertToAbsoluteUrls === undefined && sourceMap;

	if (options.convertToAbsoluteUrls || autoFixUrls) {
		css = fixUrls(css);
	}

	if (sourceMap) {
		// http://stackoverflow.com/a/26603875
		css += "\n/*# sourceMappingURL=data:application/json;base64," + btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))) + " */";
	}

	var blob = new Blob([css], { type: "text/css" });

	var oldSrc = link.href;

	link.href = URL.createObjectURL(blob);

	if(oldSrc) URL.revokeObjectURL(oldSrc);
}


/***/ }),

/***/ "./node_modules/style-loader/lib/urls.js":
/*!***********************************************!*\
  !*** ./node_modules/style-loader/lib/urls.js ***!
  \***********************************************/
/*! no static exports found */
/***/ (function(module, exports) {


/**
 * When source maps are enabled, `style-loader` uses a link element with a data-uri to
 * embed the css on the page. This breaks all relative urls because now they are relative to a
 * bundle instead of the current page.
 *
 * One solution is to only use full urls, but that may be impossible.
 *
 * Instead, this function "fixes" the relative urls to be absolute according to the current page location.
 *
 * A rudimentary test suite is located at `test/fixUrls.js` and can be run via the `npm test` command.
 *
 */

module.exports = function (css) {
  // get current location
  var location = typeof window !== "undefined" && window.location;

  if (!location) {
    throw new Error("fixUrls requires window.location");
  }

	// blank or null?
	if (!css || typeof css !== "string") {
	  return css;
  }

  var baseUrl = location.protocol + "//" + location.host;
  var currentDir = baseUrl + location.pathname.replace(/\/[^\/]*$/, "/");

	// convert each url(...)
	/*
	This regular expression is just a way to recursively match brackets within
	a string.

	 /url\s*\(  = Match on the word "url" with any whitespace after it and then a parens
	   (  = Start a capturing group
	     (?:  = Start a non-capturing group
	         [^)(]  = Match anything that isn't a parentheses
	         |  = OR
	         \(  = Match a start parentheses
	             (?:  = Start another non-capturing groups
	                 [^)(]+  = Match anything that isn't a parentheses
	                 |  = OR
	                 \(  = Match a start parentheses
	                     [^)(]*  = Match anything that isn't a parentheses
	                 \)  = Match a end parentheses
	             )  = End Group
              *\) = Match anything and then a close parens
          )  = Close non-capturing group
          *  = Match anything
       )  = Close capturing group
	 \)  = Match a close parens

	 /gi  = Get all matches, not the first.  Be case insensitive.
	 */
	var fixedCss = css.replace(/url\s*\(((?:[^)(]|\((?:[^)(]+|\([^)(]*\))*\))*)\)/gi, function(fullMatch, origUrl) {
		// strip quotes (if they exist)
		var unquotedOrigUrl = origUrl
			.trim()
			.replace(/^"(.*)"$/, function(o, $1){ return $1; })
			.replace(/^'(.*)'$/, function(o, $1){ return $1; });

		// already a full url? no change
		if (/^(#|data:|http:\/\/|https:\/\/|file:\/\/\/|\s*$)/i.test(unquotedOrigUrl)) {
		  return fullMatch;
		}

		// convert the url to a full url
		var newUrl;

		if (unquotedOrigUrl.indexOf("//") === 0) {
		  	//TODO: should we add protocol?
			newUrl = unquotedOrigUrl;
		} else if (unquotedOrigUrl.indexOf("/") === 0) {
			// path should be relative to the base url
			newUrl = baseUrl + unquotedOrigUrl; // already starts with '/'
		} else {
			// path should be relative to current directory
			newUrl = currentDir + unquotedOrigUrl.replace(/^\.\//, ""); // Strip leading './'
		}

		// send back the fixed url(...)
		return "url(" + JSON.stringify(newUrl) + ")";
	});

	// send back the fixed css
	return fixedCss;
};


/***/ }),

/***/ "./node_modules/webpack/buildin/global.js":
/*!***********************************!*\
  !*** (webpack)/buildin/global.js ***!
  \***********************************/
/*! no static exports found */
/***/ (function(module, exports) {

var g;

// This works in non-strict mode
g = (function() {
	return this;
})();

try {
	// This works if eval is allowed (see CSP)
	g = g || new Function("return this")();
} catch (e) {
	// This works if the window reference is available
	if (typeof window === "object") g = window;
}

// g can still be undefined, but nothing to do about it...
// We return undefined, instead of nothing here, so it's
// easier to handle this case. if(!global) { ...}

module.exports = g;


/***/ }),

/***/ "./thirdparty/three.js/TransformControls.js":
/*!**************************************************!*\
  !*** ./thirdparty/three.js/TransformControls.js ***!
  \**************************************************/
/*! exports provided: init_TransformGizmos */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "init_TransformGizmos", function() { return init_TransformGizmos; });
/**
 * @author arodic / https://github.com/arodic
 *
 * @author chiena -- Modified for Autodesk LMV web viewer
 */
/*jshint sub:true*/

function init_TransformGizmos() {

  'use strict';

  var GizmoMaterial = function GizmoMaterial(parameters) {

    THREE.MeshBasicMaterial.call(this);

    this.depthTest = false;
    this.depthWrite = false;
    this.side = THREE.FrontSide;
    this.transparent = true;

    this.setValues(parameters);

    this.oldColor = this.color.clone();
    this.oldOpacity = this.opacity;

    this.highlight = function (highlighted) {

      if (highlighted) {

        this.color.setRGB(1, 230 / 255, 3 / 255);
        this.opacity = 1;

      } else {

        this.color.copy(this.oldColor);
        this.opacity = this.oldOpacity;

      }

    };

  };

  GizmoMaterial.prototype = Object.create(THREE.MeshBasicMaterial.prototype);

  var GizmoLineMaterial = function GizmoLineMaterial(parameters) {

    THREE.LineBasicMaterial.call(this);

    this.depthTest = false;
    this.depthWrite = false;
    this.transparent = true;
    this.linewidth = 1;

    this.setValues(parameters);

    this.oldColor = this.color.clone();
    this.oldOpacity = this.opacity;

    this.highlight = function (highlighted) {

      if (highlighted) {

        this.color.setRGB(1, 230 / 255, 3 / 255);
        this.opacity = 1;

      } else {

        this.color.copy(this.oldColor);
        this.opacity = this.oldOpacity;

      }

    };

  };

  GizmoLineMaterial.prototype = Object.create(THREE.LineBasicMaterial.prototype);

  // polyfill
  if (THREE.PolyhedronGeometry === undefined) {
    THREE.PolyhedronGeometry = function (vertices, indices, radius, detail) {

      THREE.Geometry.call(this);

      this.type = 'PolyhedronGeometry';

      this.parameters = {
        vertices: vertices,
        indices: indices,
        radius: radius,
        detail: detail };


      radius = radius || 1;
      detail = detail || 0;

      var that = this;

      for (var i = 0, l = vertices.length; i < l; i += 3) {

        prepare(new THREE.Vector3(vertices[i], vertices[i + 1], vertices[i + 2]));

      }

      var midpoints = [],p = this.vertices;

      var faces = [];

      for (var i = 0, j = 0, l = indices.length; i < l; i += 3, j++) {

        var v1 = p[indices[i]];
        var v2 = p[indices[i + 1]];
        var v3 = p[indices[i + 2]];

        faces[j] = new THREE.Face3(v1.index, v2.index, v3.index, [v1.clone(), v2.clone(), v3.clone()]);

      }

      var centroid = new THREE.Vector3();

      for (var i = 0, l = faces.length; i < l; i++) {

        subdivide(faces[i], detail);

      }


      // Handle case when face straddles the seam

      for (var i = 0, l = this.faceVertexUvs[0].length; i < l; i++) {

        var uvs = this.faceVertexUvs[0][i];

        var x0 = uvs[0].x;
        var x1 = uvs[1].x;
        var x2 = uvs[2].x;

        var max = Math.max(x0, Math.max(x1, x2));
        var min = Math.min(x0, Math.min(x1, x2));

        if (max > 0.9 && min < 0.1) {// 0.9 is somewhat arbitrary

          if (x0 < 0.2) uvs[0].x += 1;
          if (x1 < 0.2) uvs[1].x += 1;
          if (x2 < 0.2) uvs[2].x += 1;

        }

      }


      // Apply radius

      for (var i = 0, l = this.vertices.length; i < l; i++) {

        this.vertices[i].multiplyScalar(radius);

      }


      // Merge vertices

      this.mergeVertices();

      this.computeFaceNormals();

      this.boundingSphere = new THREE.Sphere(new THREE.Vector3(), radius);


      // Project vector onto sphere's surface

      function prepare(vector) {

        var vertex = vector.normalize().clone();
        vertex.index = that.vertices.push(vertex) - 1;

        // Texture coords are equivalent to map coords, calculate angle and convert to fraction of a circle.

        var u = azimuth(vector) / 2 / Math.PI + 0.5;
        var v = inclination(vector) / Math.PI + 0.5;
        vertex.uv = new THREE.Vector2(u, 1 - v);

        return vertex;

      }


      // Approximate a curved face with recursively sub-divided triangles.

      function make(v1, v2, v3) {

        var face = new THREE.Face3(v1.index, v2.index, v3.index, [v1.clone(), v2.clone(), v3.clone()]);
        that.faces.push(face);

        centroid.copy(v1).add(v2).add(v3).divideScalar(3);

        var azi = azimuth(centroid);

        that.faceVertexUvs[0].push([
        correctUV(v1.uv, v1, azi),
        correctUV(v2.uv, v2, azi),
        correctUV(v3.uv, v3, azi)]);


      }


      // Analytically subdivide a face to the required detail level.

      function subdivide(face, detail) {

        var cols = Math.pow(2, detail);
        var cells = Math.pow(4, detail);
        var a = prepare(that.vertices[face.a]);
        var b = prepare(that.vertices[face.b]);
        var c = prepare(that.vertices[face.c]);
        var v = [];

        // Construct all of the vertices for this subdivision.

        for (var i = 0; i <= cols; i++) {

          v[i] = [];

          var aj = prepare(a.clone().lerp(c, i / cols));
          var bj = prepare(b.clone().lerp(c, i / cols));
          var rows = cols - i;

          for (var j = 0; j <= rows; j++) {

            if (j == 0 && i == cols) {

              v[i][j] = aj;

            } else {

              v[i][j] = prepare(aj.clone().lerp(bj, j / rows));

            }

          }

        }

        // Construct all of the faces.

        for (var i = 0; i < cols; i++) {

          for (var j = 0; j < 2 * (cols - i) - 1; j++) {

            var k = Math.floor(j / 2);

            if (j % 2 == 0) {

              make(
              v[i][k + 1],
              v[i + 1][k],
              v[i][k]);


            } else {

              make(
              v[i][k + 1],
              v[i + 1][k + 1],
              v[i + 1][k]);


            }

          }

        }

      }


      // Angle around the Y axis, counter-clockwise when looking from above.

      function azimuth(vector) {

        return Math.atan2(vector.z, -vector.x);

      }


      // Angle above the XZ plane.

      function inclination(vector) {

        return Math.atan2(-vector.y, Math.sqrt(vector.x * vector.x + vector.z * vector.z));

      }


      // Texture fixing helper. Spheres have some odd behaviours.

      function correctUV(uv, vector, azimuth) {

        if (azimuth < 0 && uv.x === 1) uv = new THREE.Vector2(uv.x - 1, uv.y);
        if (vector.x === 0 && vector.z === 0) uv = new THREE.Vector2(azimuth / 2 / Math.PI + 0.5, uv.y);
        return uv.clone();

      }

    };

    THREE.PolyhedronGeometry.prototype = Object.create(THREE.Geometry.prototype);
  }

  // polyfill
  if (THREE.OctahedronGeometry === undefined) {
    THREE.OctahedronGeometry = function (radius, detail) {

      this.parameters = {
        radius: radius,
        detail: detail };


      var vertices = [
      1, 0, 0, -1, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 1, 0, 0, -1];


      var indices = [
      0, 2, 4, 0, 4, 3, 0, 3, 5, 0, 5, 2, 1, 2, 5, 1, 5, 3, 1, 3, 4, 1, 4, 2];


      THREE.PolyhedronGeometry.call(this, vertices, indices, radius, detail);

      this.type = 'OctahedronGeometry';

      this.parameters = {
        radius: radius,
        detail: detail };

    };

    THREE.OctahedronGeometry.prototype = Object.create(THREE.Geometry.prototype);
  }

  // polyfill
  if (THREE.TorusGeometry === undefined) {
    THREE.TorusGeometry = function (radius, tube, radialSegments, tubularSegments, arc) {

      THREE.Geometry.call(this);

      this.type = 'TorusGeometry';

      this.parameters = {
        radius: radius,
        tube: tube,
        radialSegments: radialSegments,
        tubularSegments: tubularSegments,
        arc: arc };


      radius = radius || 100;
      tube = tube || 40;
      radialSegments = radialSegments || 8;
      tubularSegments = tubularSegments || 6;
      arc = arc || Math.PI * 2;

      var center = new THREE.Vector3(),uvs = [],normals = [];

      for (var j = 0; j <= radialSegments; j++) {

        for (var i = 0; i <= tubularSegments; i++) {

          var u = i / tubularSegments * arc;
          var v = j / radialSegments * Math.PI * 2;

          center.x = radius * Math.cos(u);
          center.y = radius * Math.sin(u);

          var vertex = new THREE.Vector3();
          vertex.x = (radius + tube * Math.cos(v)) * Math.cos(u);
          vertex.y = (radius + tube * Math.cos(v)) * Math.sin(u);
          vertex.z = tube * Math.sin(v);

          this.vertices.push(vertex);

          uvs.push(new THREE.Vector2(i / tubularSegments, j / radialSegments));
          normals.push(vertex.clone().sub(center).normalize());

        }

      }

      for (var j = 1; j <= radialSegments; j++) {

        for (var i = 1; i <= tubularSegments; i++) {

          var a = (tubularSegments + 1) * j + i - 1;
          var b = (tubularSegments + 1) * (j - 1) + i - 1;
          var c = (tubularSegments + 1) * (j - 1) + i;
          var d = (tubularSegments + 1) * j + i;

          var face = new THREE.Face3(a, b, d, [normals[a].clone(), normals[b].clone(), normals[d].clone()]);
          this.faces.push(face);
          this.faceVertexUvs[0].push([uvs[a].clone(), uvs[b].clone(), uvs[d].clone()]);

          face = new THREE.Face3(b, c, d, [normals[b].clone(), normals[c].clone(), normals[d].clone()]);
          this.faces.push(face);
          this.faceVertexUvs[0].push([uvs[b].clone(), uvs[c].clone(), uvs[d].clone()]);

        }

      }

      this.computeFaceNormals();

    };

    THREE.TorusGeometry.prototype = Object.create(THREE.Geometry.prototype);
  }

  var createCircleGeometry = function createCircleGeometry(radius, facing, arc) {

    var geometry = new THREE.Geometry();
    arc = arc ? arc : 1;
    for (var i = 0; i <= 64 * arc; ++i) {
      if (facing == 'x') geometry.vertices.push(new THREE.Vector3(0, Math.cos(i / 32 * Math.PI), Math.sin(i / 32 * Math.PI)).multiplyScalar(radius));
      if (facing == 'y') geometry.vertices.push(new THREE.Vector3(Math.cos(i / 32 * Math.PI), 0, Math.sin(i / 32 * Math.PI)).multiplyScalar(radius));
      if (facing == 'z') geometry.vertices.push(new THREE.Vector3(Math.sin(i / 32 * Math.PI), Math.cos(i / 32 * Math.PI), 0).multiplyScalar(radius));
    }

    return geometry;
  };

  var createArrowGeometry = function createArrowGeometry(radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded) {

    var arrowGeometry = new THREE.Geometry();
    var mesh = new THREE.Mesh(new THREE.CylinderGeometry(radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded));
    mesh.position.y = 0.5;
    mesh.updateMatrix();

    arrowGeometry.merge(mesh.geometry, mesh.matrix);

    return arrowGeometry;
  };

  var createLineGeometry = function createLineGeometry(axis) {

    var lineGeometry = new THREE.Geometry();
    if (axis === 'X')
    lineGeometry.vertices.push(new THREE.Vector3(0, 0, 0), new THREE.Vector3(1, 0, 0));else
    if (axis === 'Y')
    lineGeometry.vertices.push(new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 1, 0));else
    if (axis === 'Z')
    lineGeometry.vertices.push(new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0, 1));

    return lineGeometry;
  };

  THREE.TransformGizmo = function () {

    var scope = this;
    var showPickers = false; //debug
    var showActivePlane = false; //debug

    this.init = function () {

      THREE.Object3D.call(this);

      this.handles = new THREE.Object3D();
      this.pickers = new THREE.Object3D();
      this.planes = new THREE.Object3D();
      this.highlights = new THREE.Object3D();
      this.hemiPicker = new THREE.Object3D();
      this.subPickers = new THREE.Object3D();

      this.add(this.handles);
      this.add(this.pickers);
      this.add(this.planes);
      this.add(this.highlights);
      this.add(this.hemiPicker);
      this.add(this.subPickers);

      //// PLANES

      var planeGeometry = new THREE.PlaneBufferGeometry(50, 50, 2, 2);
      var planeMaterial = new THREE.MeshBasicMaterial({ wireframe: true });
      planeMaterial.side = THREE.DoubleSide;

      var planes = {
        "XY": new THREE.Mesh(planeGeometry, planeMaterial),
        "YZ": new THREE.Mesh(planeGeometry, planeMaterial),
        "XZ": new THREE.Mesh(planeGeometry, planeMaterial),
        "XYZE": new THREE.Mesh(planeGeometry, planeMaterial) };


      this.activePlane = planes["XYZE"];

      planes["YZ"].rotation.set(0, Math.PI / 2, 0);
      planes["XZ"].rotation.set(-Math.PI / 2, 0, 0);

      for (var i in planes) {
        planes[i].name = i;
        this.planes.add(planes[i]);
        this.planes[i] = planes[i];
        planes[i].visible = false;
      }

      this.setupGizmos();
      this.activeMode = "";

      // reset Transformations

      this.traverse(function (child) {
        if (child instanceof THREE.Mesh) {
          child.updateMatrix();

          var tempGeometry = new THREE.Geometry();
          if (child.geometry instanceof THREE.BufferGeometry) {
            child.geometry = new THREE.Geometry().fromBufferGeometry(child.geometry);
          }
          tempGeometry.merge(child.geometry, child.matrix);

          child.geometry = tempGeometry;
          child.position.set(0, 0, 0);
          child.rotation.set(0, 0, 0);
          child.scale.set(1, 1, 1);
        }
      });

    };

    this.hide = function () {
      this.traverse(function (child) {
        child.visible = false;
      });
    };

    this.show = function () {
      this.traverse(function (child) {
        child.visible = true;
        if (child.parent == scope.pickers || child.parent == scope.hemiPicker) child.visible = showPickers;
        if (child.parent == scope.planes) child.visible = false;
      });
      this.activePlane.visible = showActivePlane;
    };

    this.highlight = function (axis) {
      this.traverse(function (child) {
        if (child.material && child.material.highlight) {
          if (child.name == axis) {
            child.material.highlight(true);
          } else {
            child.material.highlight(false);
          }
        }
      });
    };

    this.setupGizmos = function () {

      var addGizmos = function addGizmos(gizmoMap, parent) {

        for (var name in gizmoMap) {

          for (var i = gizmoMap[name].length; i--;) {

            var object = gizmoMap[name][i][0];
            var position = gizmoMap[name][i][1];
            var rotation = gizmoMap[name][i][2];
            var visble = gizmoMap[name][i][3];

            object.name = name;

            if (position) object.position.set(position[0], position[1], position[2]);
            if (rotation) object.rotation.set(rotation[0], rotation[1], rotation[2]);
            if (visble) object.visble = visble;

            parent.add(object);

          }

        }

      };

      this.setHandlePickerGizmos();

      addGizmos(this.handleGizmos, this.handles);
      addGizmos(this.pickerGizmos, this.pickers);
      addGizmos(this.highlightGizmos, this.highlights);
      addGizmos(this.hemiPickerGizmos, this.hemiPicker);
      addGizmos(this.subPickerGizmos, this.subPickers);

      this.hide();
      this.show();

    };

  };

  THREE.TransformGizmo.prototype = Object.create(THREE.Object3D.prototype);

  THREE.TransformGizmo.prototype.update = function (rotation, eye) {

    var vec1 = new THREE.Vector3(0, 0, 0);
    var vec2 = new THREE.Vector3(0, 1, 0);
    var lookAtMatrix = new THREE.Matrix4();

    this.traverse(function (child) {
      if (child.name) {
        if (child.name.search("E") != -1) {
          child.quaternion.setFromRotationMatrix(lookAtMatrix.lookAt(eye, vec1, vec2));
        } else if (child.name.search("X") != -1 || child.name.search("Y") != -1 || child.name.search("Z") != -1) {
          child.quaternion.setFromEuler(rotation);
        }
      }
    });

  };

  THREE.TransformGizmoTranslate = function () {

    THREE.TransformGizmo.call(this);

    this.setHandlePickerGizmos = function () {

      var arrowGeometry = createArrowGeometry(0, 0.05, 0.2, 12, 1, false);
      var lineXGeometry = createLineGeometry('X');
      var lineYGeometry = createLineGeometry('Y');
      var lineZGeometry = createLineGeometry('Z');

      this.handleGizmos = {
        X: [
        [new THREE.Mesh(arrowGeometry, new GizmoMaterial({ color: 0xf12c2c })), [0.5, 0, 0], [0, 0, -Math.PI / 2]],
        [new THREE.Line(lineXGeometry, new GizmoLineMaterial({ color: 0xf12c2c }))]],

        Y: [
        [new THREE.Mesh(arrowGeometry, new GizmoMaterial({ color: 0x0bb80b })), [0, 0.5, 0]],
        [new THREE.Line(lineYGeometry, new GizmoLineMaterial({ color: 0x0bb80b }))]],

        Z: [
        [new THREE.Mesh(arrowGeometry, new GizmoMaterial({ color: 0x2c2cf1 })), [0, 0, 0.5], [Math.PI / 2, 0, 0]],
        [new THREE.Line(lineZGeometry, new GizmoLineMaterial({ color: 0x2c2cf1 }))]],

        XYZ: [
        [new THREE.Mesh(new THREE.OctahedronGeometry(0.1, 0), new GizmoMaterial({ color: 0xffffff, opacity: 0.25 })), [0, 0, 0], [0, 0, 0]]],

        XY: [
        [new THREE.Mesh(new THREE.PlaneBufferGeometry(0.29, 0.29), new GizmoMaterial({ color: 0xffff00, opacity: 0.25 })), [0.15, 0.15, 0]]],

        YZ: [
        [new THREE.Mesh(new THREE.PlaneBufferGeometry(0.29, 0.29), new GizmoMaterial({ color: 0x00ffff, opacity: 0.25 })), [0, 0.15, 0.15], [0, Math.PI / 2, 0]]],

        XZ: [
        [new THREE.Mesh(new THREE.PlaneBufferGeometry(0.29, 0.29), new GizmoMaterial({ color: 0xff00ff, opacity: 0.25 })), [0.15, 0, 0.15], [-Math.PI / 2, 0, 0]]] };



      this.pickerGizmos = {
        X: [
        [new THREE.Mesh(new THREE.CylinderGeometry(0.2, 0, 1, 4, 1, false), new GizmoMaterial({ color: 0xff0000, opacity: 0.25 })), [0.6, 0, 0], [0, 0, -Math.PI / 2]]],

        Y: [
        [new THREE.Mesh(new THREE.CylinderGeometry(0.2, 0, 1, 4, 1, false), new GizmoMaterial({ color: 0x00ff00, opacity: 0.25 })), [0, 0.6, 0]]],

        Z: [
        [new THREE.Mesh(new THREE.CylinderGeometry(0.2, 0, 1, 4, 1, false), new GizmoMaterial({ color: 0x0000ff, opacity: 0.25 })), [0, 0, 0.6], [Math.PI / 2, 0, 0]]],

        XYZ: [
        [new THREE.Mesh(new THREE.OctahedronGeometry(0.2, 0), new GizmoMaterial({ color: 0xffffff, opacity: 0.25 }))]],

        XY: [
        [new THREE.Mesh(new THREE.PlaneBufferGeometry(0.4, 0.4), new GizmoMaterial({ color: 0xffff00, opacity: 0.25 })), [0.2, 0.2, 0]]],

        YZ: [
        [new THREE.Mesh(new THREE.PlaneBufferGeometry(0.4, 0.4), new GizmoMaterial({ color: 0x00ffff, opacity: 0.25 })), [0, 0.2, 0.2], [0, Math.PI / 2, 0]]],

        XZ: [
        [new THREE.Mesh(new THREE.PlaneBufferGeometry(0.4, 0.4), new GizmoMaterial({ color: 0xff00ff, opacity: 0.25 })), [0.2, 0, 0.2], [-Math.PI / 2, 0, 0]]] };



      this.hemiPickerGizmos = {
        XYZ: [
        [new THREE.Mesh(new THREE.BoxGeometry(1.2, 1.2, 1.2), new GizmoMaterial({ color: 0x0000ff })), [0.5, 0.5, 0.5], null, false]] };



    };

    this.setActivePlane = function (axis, eye) {

      var tempMatrix = new THREE.Matrix4();
      eye.applyMatrix4(tempMatrix.getInverse(tempMatrix.extractRotation(this.planes["XY"].matrixWorld)));

      if (axis == "X") {
        this.activePlane = this.planes["XY"];
        if (Math.abs(eye.y) > Math.abs(eye.z)) this.activePlane = this.planes["XZ"];
      }

      if (axis == "Y") {
        this.activePlane = this.planes["XY"];
        if (Math.abs(eye.x) > Math.abs(eye.z)) this.activePlane = this.planes["YZ"];
      }

      if (axis == "Z") {
        this.activePlane = this.planes["XZ"];
        if (Math.abs(eye.x) > Math.abs(eye.y)) this.activePlane = this.planes["YZ"];
      }

      if (axis == "XYZ") this.activePlane = this.planes["XYZE"];

      if (axis == "XY") this.activePlane = this.planes["XY"];

      if (axis == "YZ") this.activePlane = this.planes["YZ"];

      if (axis == "XZ") this.activePlane = this.planes["XZ"];

      this.hide();
      this.show();

    };

    this.init();

  };

  THREE.TransformGizmoTranslate.prototype = Object.create(THREE.TransformGizmo.prototype);

  THREE.TransformGizmoRotate = function () {

    THREE.TransformGizmo.call(this);

    this.setHandlePickerGizmos = function () {

      this.handleGizmos = {
        RX: [
        [new THREE.Line(createCircleGeometry(1, 'x', 0.5), new GizmoLineMaterial({ color: 0xff0000 }))]],

        RY: [
        [new THREE.Line(createCircleGeometry(1, 'y', 0.5), new GizmoLineMaterial({ color: 0x00ff00 }))]],

        RZ: [
        [new THREE.Line(createCircleGeometry(1, 'z', 0.5), new GizmoLineMaterial({ color: 0x0000ff }))]],

        RE: [
        [new THREE.Line(createCircleGeometry(1.25, 'z', 1), new GizmoLineMaterial({ color: 0x00ffff }))]],

        RXYZE: [
        [new THREE.Line(createCircleGeometry(1, 'z', 1), new GizmoLineMaterial({ color: 0xff00ff }))]] };



      this.pickerGizmos = {
        RX: [
        [new THREE.Mesh(new THREE.TorusGeometry(1, 0.12, 4, 12, Math.PI), new GizmoMaterial({ color: 0xff0000, opacity: 0.25 })), [0, 0, 0], [0, -Math.PI / 2, -Math.PI / 2]]],

        RY: [
        [new THREE.Mesh(new THREE.TorusGeometry(1, 0.12, 4, 12, Math.PI), new GizmoMaterial({ color: 0x00ff00, opacity: 0.25 })), [0, 0, 0], [Math.PI / 2, 0, 0]]],

        RZ: [
        [new THREE.Mesh(new THREE.TorusGeometry(1, 0.12, 4, 12, Math.PI), new GizmoMaterial({ color: 0x0000ff, opacity: 0.25 })), [0, 0, 0], [0, 0, -Math.PI / 2]]],

        RE: [
        [new THREE.Mesh(new THREE.TorusGeometry(1.25, 0.12, 2, 24), new GizmoMaterial({ color: 0x00ffff, opacity: 0.25 }))]],

        RXYZE: [
        [new THREE.Mesh(new THREE.TorusGeometry(1, 0.12, 2, 24), new GizmoMaterial({ color: 0xff00ff, opacity: 0.25 }))]] };



    };

    this.setActivePlane = function (axis) {

      if (axis == "RE") this.activePlane = this.planes["XYZE"];

      if (axis == "RX") this.activePlane = this.planes["YZ"];

      if (axis == "RY") this.activePlane = this.planes["XZ"];

      if (axis == "RZ") this.activePlane = this.planes["XY"];

      this.hide();
      this.show();

    };

    this.update = function (rotation, eye2) {

      THREE.TransformGizmo.prototype.update.apply(this, arguments);

      var tempMatrix = new THREE.Matrix4();
      var worldRotation = new THREE.Euler(0, 0, 1);
      var tempQuaternion = new THREE.Quaternion();
      var unitX = new THREE.Vector3(1, 0, 0);
      var unitY = new THREE.Vector3(0, 1, 0);
      var unitZ = new THREE.Vector3(0, 0, 1);
      var quaternionX = new THREE.Quaternion();
      var quaternionY = new THREE.Quaternion();
      var quaternionZ = new THREE.Quaternion();
      var eye = eye2.clone();

      worldRotation.copy(this.planes["XY"].rotation);
      tempQuaternion.setFromEuler(worldRotation);

      tempMatrix.makeRotationFromQuaternion(tempQuaternion).getInverse(tempMatrix);
      eye.applyMatrix4(tempMatrix);

      this.traverse(function (child) {

        tempQuaternion.setFromEuler(worldRotation);

        if (child.name == "RX") {
          quaternionX.setFromAxisAngle(unitX, Math.atan2(-eye.y, eye.z));
          tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionX);
          child.quaternion.copy(tempQuaternion);
        }

        if (child.name == "RY") {
          quaternionY.setFromAxisAngle(unitY, Math.atan2(eye.x, eye.z));
          tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionY);
          child.quaternion.copy(tempQuaternion);
        }

        if (child.name == "RZ") {
          quaternionZ.setFromAxisAngle(unitZ, Math.atan2(eye.y, eye.x));
          tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionZ);
          child.quaternion.copy(tempQuaternion);
        }

      });

    };

    this.init();

  };

  THREE.TransformGizmoRotate.prototype = Object.create(THREE.TransformGizmo.prototype);

  THREE.TransformGizmoTranslateRotate = function () {

    THREE.TransformGizmo.call(this);

    var scope = this;

    this.setHandlePickerGizmos = function () {

      var arrowGeometry = createArrowGeometry(0, 0.05, 0.2, 12, 1, false);
      var lineGeometry = new THREE.Geometry();
      lineGeometry.vertices.push(new THREE.Vector3(0, 0, -0.1), new THREE.Vector3(0, 0, 0.1), new THREE.Vector3(-0.1, 0, 0), new THREE.Vector3(0.1, 0, 0));
      var theta = 0.15;

      this.handleGizmos = {
        Z: [
        [new THREE.Mesh(arrowGeometry, new GizmoMaterial({ color: 0xffffff })), [0, 0, 0.25], [Math.PI / 2, 0, 0]],
        [new THREE.Mesh(new THREE.CylinderGeometry(0.015, 0.015, 0.6, 4, 1, false), new GizmoMaterial({ color: 0xffffff })), [0, 0, 0.5], [Math.PI / 2, 0, 0]]],

        RX: [
        [new THREE.Mesh(new THREE.TorusGeometry(1, 0.015, 12, 60, theta * 2 * Math.PI), new GizmoMaterial({ color: 0xff0000 })), [0, 0, 0], [theta * Math.PI, -Math.PI / 2, 0]],
        [new THREE.Mesh(new THREE.CylinderGeometry(0.05, 0.05, 0.015, 60, 1, false), new GizmoMaterial({ color: 0xff0000 })), [0, 0, 1], [Math.PI / 2, 0, 0]]],

        RY: [
        [new THREE.Mesh(new THREE.TorusGeometry(1, 0.015, 12, 60, theta * 2 * Math.PI), new GizmoMaterial({ color: 0x0000ff })), [0, 0, 0], [Math.PI / 2, 0, (0.5 - theta) * Math.PI]],
        [new THREE.Mesh(new THREE.CylinderGeometry(0.05, 0.05, 0.01, 60, 1, false), new GizmoMaterial({ color: 0x0000ff })), [0, 0, 1]]] };



      this.pickerGizmos = {
        Z: [
        [new THREE.Mesh(new THREE.CylinderGeometry(0.12, 0.12, 0.65, 4, 1, false), new GizmoMaterial({ color: 0x0000ff, opacity: 0.25 })), [0, 0, 0.5], [Math.PI / 2, 0, 0]]],

        RX: [
        [new THREE.Mesh(new THREE.TorusGeometry(1, 0.12, 4, 12, theta * 2 * Math.PI), new GizmoMaterial({ color: 0xff0000, opacity: 0.25 })), [0, 0, 0], [theta * Math.PI, -Math.PI / 2, 0]]],

        RY: [
        [new THREE.Mesh(new THREE.TorusGeometry(1, 0.12, 4, 12, theta * 2 * Math.PI), new GizmoMaterial({ color: 0x0000ff, opacity: 0.25 })), [0, 0, 0], [Math.PI / 2, 0, (0.5 - theta) * Math.PI]]] };



      this.subPickerGizmos = {
        Z: [
        [new THREE.Mesh(new THREE.CylinderGeometry(0.12, 0.12, 0.65, 4, 1, false), new GizmoMaterial({ color: 0x0000ff, opacity: 0.25 })), [0, 0, 0.5], [Math.PI / 2, 0, 0]]] };



      this.highlightGizmos = {
        Z: [],

        RX: [
        [new THREE.Mesh(new THREE.TorusGeometry(1, 0.02, 12, 60, 2 * Math.PI), new GizmoMaterial({ color: 0xff0000, opacity: 1 })), [0, 0, 0], [0, -Math.PI / 2, -Math.PI / 2], false]],

        RY: [
        [new THREE.Mesh(new THREE.TorusGeometry(1, 0.02, 12, 60, 2 * Math.PI), new GizmoMaterial({ color: 0x0000ff, opacity: 1 })), [0, 0, 0], [Math.PI / 2, 0, 0], false]] };



      this.hemiPickerGizmos = {
        XYZ: [
        [new THREE.Mesh(new THREE.SphereGeometry(1.2, 8, 8, 0, Math.PI), new GizmoMaterial({ color: 0x0000ff })), null, null, false]] };



    };

    this.setActivePlane = function (axis, eye) {

      if (this.activeMode == "translate") {

        var tempMatrix = new THREE.Matrix4();
        eye.applyMatrix4(tempMatrix.getInverse(tempMatrix.extractRotation(this.planes["XY"].matrixWorld)));

        if (axis == "X") {
          this.activePlane = this.planes["XY"];
          if (Math.abs(eye.y) > Math.abs(eye.z)) this.activePlane = this.planes["XZ"];
        }

        if (axis == "Y") {
          this.activePlane = this.planes["XY"];
          if (Math.abs(eye.x) > Math.abs(eye.z)) this.activePlane = this.planes["YZ"];
        }

        if (axis == "Z") {
          this.activePlane = this.planes["XZ"];
          if (Math.abs(eye.x) > Math.abs(eye.y)) this.activePlane = this.planes["YZ"];
        }

      } else if (this.activeMode == "rotate") {

        if (axis == "RX") this.activePlane = this.planes["YZ"];

        if (axis == "RY") this.activePlane = this.planes["XZ"];

        if (axis == "RZ") this.activePlane = this.planes["XY"];

      }

      this.hide();
      this.show();

    };

    this.update = function (rotation, eye2) {

      if (this.activeMode == "translate") {

        THREE.TransformGizmo.prototype.update.apply(this, arguments);

      } else if (this.activeMode == "rotate") {

        THREE.TransformGizmo.prototype.update.apply(this, arguments);

        var tempMatrix = new THREE.Matrix4();
        var worldRotation = new THREE.Euler(0, 0, 1);
        var tempQuaternion = new THREE.Quaternion();
        var unitX = new THREE.Vector3(1, 0, 0);
        var unitY = new THREE.Vector3(0, 1, 0);
        var unitZ = new THREE.Vector3(0, 0, 1);
        var quaternionX = new THREE.Quaternion();
        var quaternionY = new THREE.Quaternion();
        var quaternionZ = new THREE.Quaternion();
        var eye = eye2.clone();

        worldRotation.copy(this.planes["XY"].rotation);
        tempQuaternion.setFromEuler(worldRotation);

        tempMatrix.makeRotationFromQuaternion(tempQuaternion).getInverse(tempMatrix);
        eye.applyMatrix4(tempMatrix);

        this.traverse(function (child) {

          tempQuaternion.setFromEuler(worldRotation);

          if (child.name == "RX") {
            quaternionX.setFromAxisAngle(unitX, Math.atan2(-eye.y, eye.z));
            tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionX);
            child.quaternion.copy(tempQuaternion);
          }

          if (child.name == "RY") {
            quaternionY.setFromAxisAngle(unitY, Math.atan2(eye.x, eye.z));
            tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionY);
            child.quaternion.copy(tempQuaternion);
          }

          if (child.name == "RZ") {
            quaternionZ.setFromAxisAngle(unitZ, Math.atan2(eye.y, eye.x));
            tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionZ);
            child.quaternion.copy(tempQuaternion);
          }

        });

      }

    };

    this.show = function () {
      this.traverse(function (child) {
        if (scope.parent == null || scope.parent.useAllPickers || child.parent != scope.handles) child.visible = true;
        if (child.material) child.material.opacity = child.material.oldOpacity;
        if (child.parent == scope.pickers || child.parent == scope.hemiPicker || child.parent == scope.subPickers) child.visible = false;
        if (child.parent == scope.planes || child.parent == scope.highlights) child.visible = false;
      });
      this.activePlane.visible = false;
    };

    this.highlight = function (axis) {
      this.traverse(function (child) {
        if (child.material && child.material.highlight) {
          if (child.name == axis) {
            if (child.parent == scope.highlights || child.parent == scope.handles) child.visible = true;
            child.material.highlight(true);
          } else {
            child.material.highlight(false);
            child.material.opacity = 0.1;
          }
        }
      });
    };

    this.init();

  };

  THREE.TransformGizmoTranslateRotate.prototype = Object.create(THREE.TransformGizmo.prototype);

  THREE.TransformGizmoScale = function () {

    THREE.TransformGizmo.call(this);

    this.setHandlePickerGizmos = function () {

      var arrowGeometry = createArrowGeometry(0.125, 0.125, 0.125);
      var lineXGeometry = createLineGeometry('X');
      var lineYGeometry = createLineGeometry('Y');
      var lineZGeometry = createLineGeometry('Z');

      this.handleGizmos = {
        X: [
        [new THREE.Mesh(arrowGeometry, new GizmoMaterial({ color: 0xff0000 })), [0.5, 0, 0], [0, 0, -Math.PI / 2]],
        [new THREE.Line(lineXGeometry, new GizmoLineMaterial({ color: 0xff0000 }))]],

        Y: [
        [new THREE.Mesh(arrowGeometry, new GizmoMaterial({ color: 0x00ff00 })), [0, 0.5, 0]],
        [new THREE.Line(lineYGeometry, new GizmoLineMaterial({ color: 0x00ff00 }))]],

        Z: [
        [new THREE.Mesh(arrowGeometry, new GizmoMaterial({ color: 0x0000ff })), [0, 0, 0.5], [Math.PI / 2, 0, 0]],
        [new THREE.Line(lineZGeometry, new GizmoLineMaterial({ color: 0x0000ff }))]],

        XYZ: [
        [new THREE.Mesh(new THREE.BoxGeometry(0.125, 0.125, 0.125), new GizmoMaterial({ color: 0xffffff, opacity: 0.25 }))]] };



      this.pickerGizmos = {
        X: [
        [new THREE.Mesh(new THREE.CylinderGeometry(0.2, 0, 1, 4, 1, false), new GizmoMaterial({ color: 0xff0000, opacity: 0.25 })), [0.6, 0, 0], [0, 0, -Math.PI / 2]]],

        Y: [
        [new THREE.Mesh(new THREE.CylinderGeometry(0.2, 0, 1, 4, 1, false), new GizmoMaterial({ color: 0x00ff00, opacity: 0.25 })), [0, 0.6, 0]]],

        Z: [
        [new THREE.Mesh(new THREE.CylinderGeometry(0.2, 0, 1, 4, 1, false), new GizmoMaterial({ color: 0x0000ff, opacity: 0.25 })), [0, 0, 0.6], [Math.PI / 2, 0, 0]]],

        XYZ: [
        [new THREE.Mesh(new THREE.BoxGeometry(0.4, 0.4, 0.4), new GizmoMaterial({ color: 0xffffff, opacity: 0.25 }))]] };



    };

    this.setActivePlane = function (axis, eye) {

      var tempMatrix = new THREE.Matrix4();
      eye.applyMatrix4(tempMatrix.getInverse(tempMatrix.extractRotation(this.planes["XY"].matrixWorld)));

      if (axis == "X") {
        this.activePlane = this.planes["XY"];
        if (Math.abs(eye.y) > Math.abs(eye.z)) this.activePlane = this.planes["XZ"];
      }

      if (axis == "Y") {
        this.activePlane = this.planes["XY"];
        if (Math.abs(eye.x) > Math.abs(eye.z)) this.activePlane = this.planes["YZ"];
      }

      if (axis == "Z") {
        this.activePlane = this.planes["XZ"];
        if (Math.abs(eye.x) > Math.abs(eye.y)) this.activePlane = this.planes["YZ"];
      }

      if (axis == "XYZ") this.activePlane = this.planes["XYZE"];

      this.hide();
      this.show();

    };

    this.init();

  };

  THREE.TransformGizmoScale.prototype = Object.create(THREE.TransformGizmo.prototype);

  THREE.TransformControls = function (camera, domElement, mode) {

    // TODO: Make non-uniform scale and rotate play nice in hierarchies
    // TODO: ADD RXYZ contol

    THREE.Object3D.call(this);

    domElement = domElement !== undefined ? domElement : document;

    this.gizmo = {};
    switch (mode) {
      case "translate":
        this.gizmo[mode] = new THREE.TransformGizmoTranslate();
        break;
      case "rotate":
        this.gizmo[mode] = new THREE.TransformGizmoRotate();
        break;
      case "transrotate":
        this.gizmo[mode] = new THREE.TransformGizmoTranslateRotate();
        break;
      case "scale":
        this.gizmo[mode] = new THREE.TransformGizmoScale();
        break;}


    this.add(this.gizmo[mode]);
    this.gizmo[mode].hide();

    this.object = undefined;
    this.snap = null;
    this.snapDelta = 0;
    this.space = "world";
    this.size = 1;
    this.axis = null;
    this.useAllPickers = true;

    this.unitX = new THREE.Vector3(1, 0, 0);
    this.unitY = new THREE.Vector3(0, 1, 0);
    this.unitZ = new THREE.Vector3(0, 0, 1);
    this.normal = new THREE.Vector3(0, 0, 1);

    if (mode === "transrotate") {
      var geometry = new THREE.Geometry();
      geometry.vertices.push(new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0, 1));
      var material = new THREE.LineBasicMaterial({ color: 0x000000, linewidth: 2, depthTest: false });
      this.startLine = new THREE.Line(geometry, material);
      var geometry = new THREE.Geometry();
      var material = new THREE.LineBasicMaterial({ color: 0xffe603, linewidth: 2, depthTest: false });
      geometry.vertices.push(new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 0, 1));
      this.endLine = new THREE.Line(geometry, material);
      var geometry = new THREE.Geometry();
      var material = new THREE.LineDashedMaterial({ color: 0x000000, linewidth: 1, depthTest: false });
      geometry.vertices.push(new THREE.Vector3(0, -1, 0), new THREE.Vector3(0, 1, 0));
      this.centerLine = new THREE.Line(geometry, material);

      var map = THREE.ImageUtils.loadTexture(Autodesk.Viewing.Private.getResourceUrl("res/textures/centerMarker_X.png"));
      map.magFilter = map.minFilter = THREE.NearestFilter;
      var geometry = new THREE.CircleGeometry(0.1, 32);
      var material = new THREE.MeshBasicMaterial({ opacity: 1, side: THREE.DoubleSide, transparent: true, map: map });
      this.centerMark = new THREE.Mesh(geometry, material);
      this.centerMark.rotation.set(Math.PI / 2, 0, 0);

      this.ticks = {};
      var map = THREE.ImageUtils.loadTexture(Autodesk.Viewing.Private.getResourceUrl("res/textures/cardinalPoint.png"));
      map.magFilter = map.minFilter = THREE.NearestFilter;
      var material = new THREE.MeshBasicMaterial({ depthTest: false, opacity: 1, transparent: true, side: THREE.DoubleSide, map: map });
      var w = 0.12,h = 0.25,d = 1.15;

      this.ticks["RX"] = new THREE.Object3D();
      var geometry = new THREE.PlaneBufferGeometry(w, h);
      var mesh = new THREE.Mesh(geometry, material);
      mesh.position.set(0, 0, -d - h / 2);
      mesh.rotation.set(Math.PI / 2, Math.PI / 2, 0);
      this.ticks["RX"].add(mesh);

      mesh = mesh.clone();
      mesh.position.set(0, d + h / 2, 0);
      mesh.rotation.set(0, Math.PI / 2, 0);
      this.ticks["RX"].add(mesh);

      mesh = mesh.clone();
      mesh.position.set(0, 0, d + h / 2);
      mesh.rotation.set(0, Math.PI / 2, Math.PI / 2);
      this.ticks["RX"].add(mesh);

      mesh = mesh.clone();
      mesh.position.set(0, -d - h / 2, 0);
      mesh.rotation.set(0, Math.PI / 2, 0);
      this.ticks["RX"].add(mesh);

      this.ticks["RY"] = new THREE.Object3D();
      mesh = mesh.clone();
      mesh.position.set(0, 0, -d - h / 2);
      mesh.rotation.set(Math.PI / 2, 0, 0);
      this.ticks["RY"].add(mesh);

      mesh = mesh.clone();
      mesh.position.set(-d - h / 2, 0, 0);
      mesh.rotation.set(Math.PI / 2, 0, Math.PI / 2);
      this.ticks["RY"].add(mesh);

      mesh = mesh.clone();
      mesh.position.set(0, 0, d + h / 2);
      mesh.rotation.set(Math.PI / 2, 0, 0);
      this.ticks["RY"].add(mesh);

      mesh = mesh.clone();
      mesh.position.set(d + h / 2, 0, 0);
      mesh.rotation.set(Math.PI / 2, 0, Math.PI / 2);
      this.ticks["RY"].add(mesh);
    }

    var scope = this;

    var _dragging = false;
    var _mode = mode;
    var _plane = "XY";

    var changeEvent = { type: "change" };
    var mouseDownEvent = { type: "mouseDown" };
    var mouseUpEvent = { type: "mouseUp", mode: _mode };
    var objectChangeEvent = { type: "objectChange" };

    var ray = new THREE.Raycaster();
    var pointerVector = new THREE.Vector3();
    var pointerDir = new THREE.Vector3();

    var point = new THREE.Vector3();
    var offset = new THREE.Vector3();

    var rotation = new THREE.Vector3();
    var offsetRotation = new THREE.Vector3();
    var scale = 1;
    this.clientScale = 1;

    var lookAtMatrix = new THREE.Matrix4();
    var eye = new THREE.Vector3();

    var tempMatrix = new THREE.Matrix4();
    var tempVector = new THREE.Vector3();
    var tempQuaternion = new THREE.Quaternion();
    var projX = new THREE.Vector3();
    var projY = new THREE.Vector3();
    var projZ = new THREE.Vector3();

    var quaternionXYZ = new THREE.Quaternion();
    var quaternionX = new THREE.Quaternion();
    var quaternionY = new THREE.Quaternion();
    var quaternionZ = new THREE.Quaternion();
    var quaternionE = new THREE.Quaternion();

    var oldPosition = new THREE.Vector3();
    var oldScale = new THREE.Vector3();
    var oldRotationMatrix = new THREE.Matrix4();

    var parentRotationMatrix = new THREE.Matrix4();
    var parentScale = new THREE.Vector3();

    var worldPosition = new THREE.Vector3();
    var worldRotation = new THREE.Euler();
    var worldRotationMatrix = new THREE.Matrix4();
    var camPosition = new THREE.Vector3();
    var camRotation = new THREE.Euler();

    this.attach = function (object) {

      scope.object = object;

      this.gizmo[_mode].show();

      scope.update();

      scope.updateUnitVectors();

    };

    this.detach = function (object) {

      scope.object = undefined;
      this.axis = null;

      this.gizmo[_mode].hide();

    };

    this.setMode = function (mode) {

      _mode = mode ? mode : _mode;

      if (_mode == "scale") scope.space = "local";

      this.gizmo[_mode].show();

      this.update();
      scope.dispatchEvent(changeEvent);

    };

    this.getPicker = function () {

      return scope.gizmo[_mode].hemiPicker.children;

    };

    this.setPosition = function (position) {

      this.object.position.copy(position);
      this.update();

    };

    this.setNormal = function (normal) {

      tempQuaternion.setFromUnitVectors(this.normal, normal);
      this.unitX.applyQuaternion(tempQuaternion);
      this.unitY.applyQuaternion(tempQuaternion);
      this.unitZ.applyQuaternion(tempQuaternion);
      this.normal.copy(normal);
      if (this.object) {
        this.object.quaternion.multiply(tempQuaternion);
      }
      this.update();
    };

    this.setSnap = function (snap, delta) {

      scope.snap = snap;
      scope.snapDelta = delta;

    };

    this.setSize = function (size) {

      scope.size = size;
      this.update();
      scope.dispatchEvent(changeEvent);

    };

    this.setSpace = function (space) {

      scope.space = space;
      this.update();
      scope.dispatchEvent(changeEvent);

    };

    this.update = function (highlight) {

      if (scope.object === undefined) return;

      scope.object.updateMatrixWorld();
      worldPosition.setFromMatrixPosition(scope.object.matrixWorld);
      worldRotation.setFromRotationMatrix(tempMatrix.extractRotation(scope.object.matrixWorld));

      camera.updateMatrixWorld();
      camPosition.setFromMatrixPosition(camera.matrixWorld);
      //camRotation.setFromRotationMatrix( tempMatrix.extractRotation( camera.matrixWorld ) );

      this.position.copy(worldPosition);

      this.quaternion.setFromEuler(worldRotation);

      this.normal.set(0, 0, 1);
      this.normal.applyEuler(worldRotation);

      // keep same screen height (100px)
      var height;
      if (camera.isPerspective) {
        var dist = worldPosition.distanceTo(camPosition);
        height = 2 * Math.tan(camera.fov * Math.PI / 360) * dist;
      } else {
        // orthographic, so the world height is simply top minus bottom
        height = camera.top - camera.bottom;
      }
      var rect = domElement.getBoundingClientRect();
      // multiply 100 pixels by world height for the window, divide by window height in pixels,
      // to get world height equivalent to 100 pixels.
      scale = this.clientScale * 100 * height / rect.height;
      this.scale.set(scale, scale, scale);

      //eye.copy( camPosition ).sub( worldPosition ).normalize();

      //if ( scope.space == "local" )
      //    this.gizmo[_mode].update( worldRotation, eye );
      //else if ( scope.space == "world" )
      //    this.gizmo[_mode].update( new THREE.Euler(), eye );

      if (highlight)
      this.gizmo[_mode].highlight(scope.axis);

    };

    this.updateUnitVectors = function () {

      this.unitX.set(1, 0, 0);
      this.unitY.set(0, 1, 0);
      this.unitZ.set(0, 0, 1);
      this.unitX.applyEuler(worldRotation);
      this.unitY.applyEuler(worldRotation);
      this.unitZ.applyEuler(worldRotation);

    };

    this.showRotationGizmos = function (set) {

      var handles = this.gizmo[_mode].handles.children;
      for (var i = 0; i < handles.length; i++) {
        var child = handles[i];
        child.visible = true;
        if (child.name.search("R") !== -1) child.visible = set;
      }
      this.useAllPickers = set;

    };

    this.highlight = function () {

      this.gizmo[_mode].highlight(this.axis || "Z");

    };

    this.onPointerHover = function (event) {

      if (scope.object === undefined || _dragging === true) return false;

      var pointer = event;

      var intersect = intersectObjects(pointer, scope.useAllPickers ? scope.gizmo[_mode].pickers.children : scope.gizmo[_mode].subPickers.children);

      var axis = null;
      var mode = "";

      if (intersect) {

        axis = intersect.object.name;
        mode = axis.search("R") != -1 ? "rotate" : "translate";

      }

      if (scope.axis !== axis) {

        scope.axis = axis;
        scope.gizmo[_mode].activeMode = mode;
        scope.update(true);
        scope.dispatchEvent(changeEvent);

      }

      if (scope.axis === null) {

        scope.gizmo[_mode].show();

      }

      return intersect ? true : false;

    };

    this.onPointerDown = function (event) {

      if (scope.object === undefined || _dragging === true) return false;

      var pointer = event;

      if (event.pointerType === 'touch') {

        var intersect = intersectObjects(pointer, scope.useAllPickers ? scope.gizmo[_mode].pickers.children : scope.gizmo[_mode].subPickers.children);

        var axis = null;
        var mode = "";

        if (intersect) {

          axis = intersect.object.name;
          mode = axis.search("R") != -1 ? "rotate" : "translate";

        }

        if (scope.axis !== axis) {

          scope.axis = axis;
          scope.gizmo[_mode].activeMode = mode;
        }
      }

      var intersect = null;

      if (pointer.button === 0 || pointer.button === -1 || pointer.button === undefined) {

        intersect = intersectObjects(pointer, scope.useAllPickers ? scope.gizmo[_mode].pickers.children : scope.gizmo[_mode].subPickers.children);

        if (intersect) {

          scope.dispatchEvent(mouseDownEvent);

          scope.axis = intersect.object.name;

          scope.update();

          eye.copy(camera.position).sub(worldPosition).normalize();

          scope.gizmo[_mode].setActivePlane(scope.axis, eye);

          var planeIntersect = intersectObjects(pointer, [scope.gizmo[_mode].activePlane]);

          if (planeIntersect)
          offset.copy(planeIntersect.point);

          oldPosition.copy(scope.object.position);
          oldScale.copy(scope.object.scale);

          oldRotationMatrix.extractRotation(scope.object.matrix);
          worldRotationMatrix.extractRotation(scope.object.matrixWorld);

          if (scope.object.parent) {
            parentRotationMatrix.extractRotation(scope.object.parent.matrixWorld);
            parentScale.setFromMatrixScale(tempMatrix.getInverse(scope.object.parent.matrixWorld));
          } else {
            parentRotationMatrix.extractRotation(scope.object.matrixWorld);
            parentScale.setFromMatrixScale(tempMatrix.getInverse(scope.object.matrixWorld));
          }

          // show rotation start line and ticks
          if (_mode === "transrotate" && scope.gizmo[_mode].activeMode === "rotate") {
            scope.startLine.geometry.vertices[0].set(0, 0, 0).applyMatrix4(scope.matrixWorld);
            scope.startLine.geometry.vertices[1].set(0, 0, 1).applyMatrix4(scope.matrixWorld);
            scope.startLine.geometry.verticesNeedUpdate = true;
            scope.parent.add(scope.startLine);

            var pos = scope.object.geometry.getAttribute('position');
            var pt1 = new THREE.Vector3().fromAttribute(pos, 0).applyMatrix4(scope.object.matrixWorld);
            var pt2 = new THREE.Vector3().fromAttribute(pos, 1).applyMatrix4(scope.object.matrixWorld);
            var pt3 = new THREE.Vector3().fromAttribute(pos, 2).applyMatrix4(scope.object.matrixWorld);
            var pt4 = new THREE.Vector3().fromAttribute(pos, 3).applyMatrix4(scope.object.matrixWorld);
            if (scope.axis === "RX") {
              pt1.lerp(pt3, 0.5);
              pt2.lerp(pt4, 0.5);
              var dist = pt1.distanceTo(pt2);
              scope.centerLine.material.dashSize = dist / 15;
              scope.centerLine.material.gapSize = dist / 30;
              scope.centerLine.geometry.vertices[0].copy(pt1);
              scope.centerLine.geometry.vertices[1].copy(pt2);
            } else {
              pt1.lerp(pt2, 0.5);
              pt3.lerp(pt4, 0.5);
              var dist = pt1.distanceTo(pt3);
              scope.centerLine.material.dashSize = dist / 15;
              scope.centerLine.material.gapSize = dist / 30;
              scope.centerLine.geometry.vertices[0].copy(pt1);
              scope.centerLine.geometry.vertices[1].copy(pt3);
            }
            scope.centerLine.geometry.computeLineDistances();
            scope.centerLine.geometry.verticesNeedUpdate = true;
            scope.parent.add(scope.centerLine);

            scope.ticks[scope.axis].position.copy(scope.position);
            scope.ticks[scope.axis].quaternion.copy(scope.quaternion);
            scope.ticks[scope.axis].scale.copy(scope.scale);
            scope.parent.add(scope.ticks[scope.axis]);
          }

        }

      }

      _dragging = true;

      return intersect ? true : false;

    };

    this.onPointerMove = function (event) {

      if (scope.object === undefined || scope.axis === null || _dragging === false) return false;

      var pointer = event;

      var planeIntersect = intersectObjects(pointer, [scope.gizmo[_mode].activePlane]);

      if (planeIntersect)
      point.copy(planeIntersect.point);

      var mode = scope.gizmo[_mode].activeMode;
      if (mode == "translate") {

        point.sub(offset);
        point.multiply(parentScale);

        if (scope.space == "local") {

          point.applyMatrix4(tempMatrix.getInverse(worldRotationMatrix));

          projX.copy(this.unitX);
          projY.copy(this.unitY);
          projZ.copy(this.unitZ);
          tempVector.set(0, 0, 0);
          if (scope.axis.search("X") != -1) {
            projX.multiplyScalar(point.dot(this.unitX));
            tempVector.add(projX);
          }
          if (scope.axis.search("Y") != -1) {
            projY.multiplyScalar(point.dot(this.unitY));
            tempVector.add(projY);
          }
          if (scope.axis.search("Z") != -1) {
            projZ.multiplyScalar(point.dot(this.unitZ));
            tempVector.add(projZ);
          }
          point.copy(tempVector);

          point.applyMatrix4(oldRotationMatrix);

          scope.object.position.copy(oldPosition);
          scope.object.position.add(point);

        }

        if (scope.space == "world" || scope.axis.search("XYZ") != -1) {

          projX.copy(this.unitX);
          projY.copy(this.unitY);
          projZ.copy(this.unitZ);
          tempVector.set(0, 0, 0);
          if (scope.axis.search("X") != -1) {
            projX.multiplyScalar(point.dot(this.unitX));
            tempVector.add(projX);
          }
          if (scope.axis.search("Y") != -1) {
            projY.multiplyScalar(point.dot(this.unitY));
            tempVector.add(projY);
          }
          if (scope.axis.search("Z") != -1) {
            projZ.multiplyScalar(point.dot(this.unitZ));
            tempVector.add(projZ);
          }
          point.copy(tempVector);

          point.applyMatrix4(tempMatrix.getInverse(parentRotationMatrix));

          scope.object.position.copy(oldPosition);
          scope.object.position.add(point);

        }

      } else if (mode == "scale") {

        point.sub(offset);
        point.multiply(parentScale);

        if (scope.space == "local") {

          if (scope.axis == "XYZ") {

            scale = 1 + point.y / 50;

            scope.object.scale.x = oldScale.x * scale;
            scope.object.scale.y = oldScale.y * scale;
            scope.object.scale.z = oldScale.z * scale;

          } else {

            point.applyMatrix4(tempMatrix.getInverse(worldRotationMatrix));

            if (scope.axis == "X") scope.object.scale.x = oldScale.x * (1 + point.x / 50);
            if (scope.axis == "Y") scope.object.scale.y = oldScale.y * (1 + point.y / 50);
            if (scope.axis == "Z") scope.object.scale.z = oldScale.z * (1 + point.z / 50);

          }

        }

      } else if (mode == "rotate") {

        point.sub(worldPosition);
        point.multiply(parentScale);
        tempVector.copy(offset).sub(worldPosition);
        tempVector.multiply(parentScale);

        if (scope.axis == "RE") {

          point.applyMatrix4(tempMatrix.getInverse(lookAtMatrix));
          tempVector.applyMatrix4(tempMatrix.getInverse(lookAtMatrix));

          rotation.set(Math.atan2(point.z, point.y), Math.atan2(point.x, point.z), Math.atan2(point.y, point.x));
          offsetRotation.set(Math.atan2(tempVector.z, tempVector.y), Math.atan2(tempVector.x, tempVector.z), Math.atan2(tempVector.y, tempVector.x));

          tempQuaternion.setFromRotationMatrix(tempMatrix.getInverse(parentRotationMatrix));

          var rotz = rotation.z - offsetRotation.z;
          if (scope.snap !== null) {
            var rotsnap = Math.round(rotz / scope.snap) * scope.snap;
            if (Math.abs(rotsnap - rotz) < scope.snapDelta) {
              rotz = rotsnap;
            }
          }
          quaternionE.setFromAxisAngle(eye, rotz);
          quaternionXYZ.setFromRotationMatrix(worldRotationMatrix);

          tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionE);
          tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionXYZ);

          scope.object.quaternion.copy(tempQuaternion);

        } else if (scope.axis == "RXYZE") {

          var tempAxis = point.clone().cross(tempVector).normalize(); // rotation axis

          tempQuaternion.setFromRotationMatrix(tempMatrix.getInverse(parentRotationMatrix));

          var rot = -point.clone().angleTo(tempVector);
          if (scope.snap !== null) {
            var rotsnap = Math.round(rot / scope.snap) * scope.snap;
            if (Math.abs(rotsnap - rot) < scope.snapDelta) {
              rot = rotsnap;
            }
          }
          quaternionX.setFromAxisAngle(tempAxis, rot);
          quaternionXYZ.setFromRotationMatrix(worldRotationMatrix);

          tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionX);
          tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionXYZ);

          scope.object.quaternion.copy(tempQuaternion);

        } else if (scope.space == "local") {

          point.applyMatrix4(tempMatrix.getInverse(worldRotationMatrix));

          tempVector.applyMatrix4(tempMatrix.getInverse(worldRotationMatrix));

          var projx = point.dot(this.unitX),projy = point.dot(this.unitY),projz = point.dot(this.unitZ);
          var tempx = tempVector.dot(this.unitX),tempy = tempVector.dot(this.unitY),tempz = tempVector.dot(this.unitZ);
          rotation.set(Math.atan2(projz, projy), Math.atan2(projx, projz), Math.atan2(projy, projx));
          offsetRotation.set(Math.atan2(tempz, tempy), Math.atan2(tempx, tempz), Math.atan2(tempy, tempx));

          var rotx = rotation.x - offsetRotation.x;
          var roty = rotation.y - offsetRotation.y;
          var rotz = rotation.z - offsetRotation.z;
          if (scope.snap !== null) {
            if (scope.axis.search("X") != -1) {
              var rotsnap = Math.round(rotx / scope.snap) * scope.snap;
              if (Math.abs(rotsnap - rotx) < scope.snapDelta) {
                rotx = rotsnap;
              }
            }
            if (scope.axis.search("Y") != -1) {
              var rotsnap = Math.round(roty / scope.snap) * scope.snap;
              if (Math.abs(rotsnap - roty) < scope.snapDelta) {
                roty = rotsnap;
              }
            }
            if (scope.axis.search("Z") != -1) {
              var rotsnap = Math.round(rotz / scope.snap) * scope.snap;
              if (Math.abs(rotsnap - rotz) < scope.snapDelta) {
                rotz = rotsnap;
              }
            }
          }
          quaternionX.setFromAxisAngle(this.unitX, rotx);
          quaternionY.setFromAxisAngle(this.unitY, roty);
          quaternionZ.setFromAxisAngle(this.unitZ, rotz);
          quaternionXYZ.setFromRotationMatrix(oldRotationMatrix);

          if (scope.axis == "RX") quaternionXYZ.multiplyQuaternions(quaternionXYZ, quaternionX);
          if (scope.axis == "RY") quaternionXYZ.multiplyQuaternions(quaternionXYZ, quaternionY);
          if (scope.axis == "RZ") quaternionXYZ.multiplyQuaternions(quaternionXYZ, quaternionZ);

          scope.object.quaternion.copy(quaternionXYZ);

        } else if (scope.space == "world") {

          var projx = point.dot(this.unitX),projy = point.dot(this.unitY),projz = point.dot(this.unitZ);
          var tempx = tempVector.dot(this.unitX),tempy = tempVector.dot(this.unitY),tempz = tempVector.dot(this.unitZ);
          rotation.set(Math.atan2(projz, projy), Math.atan2(projx, projz), Math.atan2(projy, projx));
          offsetRotation.set(Math.atan2(tempz, tempy), Math.atan2(tempx, tempz), Math.atan2(tempy, tempx));

          tempQuaternion.setFromRotationMatrix(tempMatrix.getInverse(parentRotationMatrix));

          var rotx = rotation.x - offsetRotation.x;
          var roty = rotation.y - offsetRotation.y;
          var rotz = rotation.z - offsetRotation.z;
          if (scope.snap !== null) {
            if (scope.axis.search("X") != -1) {
              var rotsnap = Math.round(rotx / scope.snap) * scope.snap;
              if (Math.abs(rotsnap - rotx) < scope.snapDelta) {
                rotx = rotsnap;
              }
            }
            if (scope.axis.search("Y") != -1) {
              var rotsnap = Math.round(roty / scope.snap) * scope.snap;
              if (Math.abs(rotsnap - roty) < scope.snapDelta) {
                roty = rotsnap;
              }
            }
            if (scope.axis.search("Z") != -1) {
              var rotsnap = Math.round(rotz / scope.snap) * scope.snap;
              if (Math.abs(rotsnap - rotz) < scope.snapDelta) {
                rotz = rotsnap;
              }
            }
          }
          quaternionX.setFromAxisAngle(this.unitX, rotx);
          quaternionY.setFromAxisAngle(this.unitY, roty);
          quaternionZ.setFromAxisAngle(this.unitZ, rotz);
          quaternionXYZ.setFromRotationMatrix(worldRotationMatrix);

          if (scope.axis == "RX") tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionX);
          if (scope.axis == "RY") tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionY);
          if (scope.axis == "RZ") tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionZ);

          tempQuaternion.multiplyQuaternions(tempQuaternion, quaternionXYZ);

          scope.object.quaternion.copy(tempQuaternion);

        }

        // show rotation end line
        if (_mode === "transrotate") {
          scope.add(scope.endLine);
          scope.add(scope.centerMark);
        }

      }

      // update matrix
      scope.object.matrixAutoUpdate = true;

      scope.update(true);
      scope.dispatchEvent(changeEvent);
      scope.dispatchEvent(objectChangeEvent);

      return planeIntersect ? true : false;

    };

    this.onPointerUp = function (event) {

      if (_dragging && scope.axis !== null) {
        mouseUpEvent.mode = _mode;
        scope.dispatchEvent(mouseUpEvent);
      }
      _dragging = false;

      this.gizmo[_mode].show();

      this.updateUnitVectors();

      // remove rotation start/end lines
      if (_mode === "transrotate" && this.gizmo[_mode].activeMode === "rotate") {
        this.remove(this.endLine);
        this.remove(this.centerMark);
        this.parent.remove(this.centerLine);
        this.parent.remove(this.startLine);
        this.parent.remove(this.ticks[this.axis]);
      }

      return false;

    };

    function intersectObjects(pointer, objects) {
      return THREE.TransformControls.intersectObjects(pointer.canvasX, pointer.canvasY, objects, camera, true);
    }
  };

  THREE.TransformControls.intersectObjects = function () {

    var pointerVector = new THREE.Vector3();
    var pointerDir = new THREE.Vector3();
    var ray = new THREE.Raycaster();

    return function (clientX, clientY, objects, camera, recursive) {

      // Convert client to viewport coords (in [-1,1]^2)
      var x = clientX / camera.clientWidth * 2 - 1;
      var y = -(clientY / camera.clientHeight) * 2 + 1; // y-direction flips between canvas and viewport coords

      if (camera.isPerspective) {
        pointerVector.set(x, y, 0.5);
        pointerVector.unproject(camera);
        ray.set(camera.position, pointerVector.sub(camera.position).normalize());
      } else {
        pointerVector.set(x, y, -1);
        pointerVector.unproject(camera);
        pointerDir.set(0, 0, -1);
        ray.set(pointerVector, pointerDir.transformDirection(camera.matrixWorld));
      }

      var intersections = ray.intersectObjects(objects, recursive);
      return intersections[0] ? intersections[0] : null;
    };
  }();

  THREE.TransformControls.prototype = Object.create(THREE.Object3D.prototype);

};

/***/ })

/******/ });